matmalib 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- matmalib-0.0.1/Matmalib/__init__.py +0 -0
- matmalib-0.0.1/Matmalib/matmacore.py +467 -0
- matmalib-0.0.1/Matmalib/matmautil.py +80 -0
- matmalib-0.0.1/PKG-INFO +8 -0
- matmalib-0.0.1/README.md +1 -0
- matmalib-0.0.1/matmalib/__init__.py +3 -0
- matmalib-0.0.1/matmalib/calc.py +140 -0
- matmalib-0.0.1/matmalib/core.py +401 -0
- matmalib-0.0.1/matmalib/util.py +833 -0
- matmalib-0.0.1/matmalib/viz.py +639 -0
- matmalib-0.0.1/matmalib.egg-info/PKG-INFO +8 -0
- matmalib-0.0.1/matmalib.egg-info/SOURCES.txt +15 -0
- matmalib-0.0.1/matmalib.egg-info/dependency_links.txt +1 -0
- matmalib-0.0.1/matmalib.egg-info/top_level.txt +2 -0
- matmalib-0.0.1/pyproject.toml +23 -0
- matmalib-0.0.1/setup.cfg +4 -0
- matmalib-0.0.1/setup.py +14 -0
|
File without changes
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
from time import sleep
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import networkx as nx
|
|
7
|
+
import re
|
|
8
|
+
from matmautil import *
|
|
9
|
+
|
|
10
|
+
class Atom():
|
|
11
|
+
|
|
12
|
+
def __init__(self, coord:list = None, type:str = None, valence:int = None, index:int = None):
|
|
13
|
+
self.coord = None
|
|
14
|
+
self.type = None
|
|
15
|
+
self.atomic_num = None
|
|
16
|
+
self.valence = None
|
|
17
|
+
self.index = None
|
|
18
|
+
|
|
19
|
+
if self.coord is not None:
|
|
20
|
+
self.x = self.coord[0]
|
|
21
|
+
self.y = self.coord[1]
|
|
22
|
+
self.z = self.coord[2]
|
|
23
|
+
|
|
24
|
+
def set_coord (self, coord:list):
|
|
25
|
+
self.coord = coord
|
|
26
|
+
|
|
27
|
+
def set_type (self, type:str):
|
|
28
|
+
self.type = type
|
|
29
|
+
|
|
30
|
+
def set_atomic_num (self, atomic_num:int):
|
|
31
|
+
self.atomic_num = atomic_num
|
|
32
|
+
|
|
33
|
+
def set_valence (self, valence:int):
|
|
34
|
+
self.valence = valence
|
|
35
|
+
|
|
36
|
+
def set_index (self, index:int):
|
|
37
|
+
self.index = index
|
|
38
|
+
|
|
39
|
+
class Mol():
|
|
40
|
+
|
|
41
|
+
def __init__(self):
|
|
42
|
+
self.atoms = None
|
|
43
|
+
self.graph = None
|
|
44
|
+
self.path = None
|
|
45
|
+
self.conn_mat = None
|
|
46
|
+
self.xyz
|
|
47
|
+
|
|
48
|
+
def element_symbol(A):
|
|
49
|
+
|
|
50
|
+
""" A dictionary for atomic number and atomic symbol
|
|
51
|
+
:param A: either atomic number or atomic symbol for Hydrogen, Carbon, Nitrogen, Oxygen, Fluorine and Silicon
|
|
52
|
+
:return: the corresponding atomic symbol or atomic number
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
periodic_table = {'1': 'H', '2': 'He',
|
|
56
|
+
'3': 'Li', '4': 'Be', '5': 'B', '6': 'C', '7': 'N', '8': 'O', '9': 'F', '10': 'Ne',
|
|
57
|
+
'11': 'Na', '12': 'Mg', '13': 'Al', '14': 'Si', '15': 'P', '16': 'S', '17': 'Cl', '18': 'Ar',
|
|
58
|
+
'19': 'K', '20': 'Ca', '35': 'Br'
|
|
59
|
+
}
|
|
60
|
+
return periodic_table[A]
|
|
61
|
+
|
|
62
|
+
def get_atoms(self):
|
|
63
|
+
xyz_list = self.xyz
|
|
64
|
+
type_list = self.type_list
|
|
65
|
+
|
|
66
|
+
atom_list = []
|
|
67
|
+
|
|
68
|
+
index = 0
|
|
69
|
+
for xyz, type in zip(xyz_list, type_list):
|
|
70
|
+
atom = Atom(coord=xyz, type=type, index=index)
|
|
71
|
+
atom_list.append(atom)
|
|
72
|
+
|
|
73
|
+
self.atoms = atom_list
|
|
74
|
+
return atom_list
|
|
75
|
+
|
|
76
|
+
def gaussian(self):
|
|
77
|
+
|
|
78
|
+
"""
|
|
79
|
+
Parses log files from Gaussian 16 and appends information inside the log file to the mol object.
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
flags = {'freq_flag': False, 'nmr_flag': False, 'opt_flag': False, 'jcoup_flag': False, 'normal_mode': False,
|
|
84
|
+
'read_geom': False}
|
|
85
|
+
job_type = None
|
|
86
|
+
|
|
87
|
+
# temprorary variables to hold the data
|
|
88
|
+
freq = [];
|
|
89
|
+
ints = [];
|
|
90
|
+
vibs = [];
|
|
91
|
+
geom = [];
|
|
92
|
+
atoms = [];
|
|
93
|
+
nmr = []
|
|
94
|
+
self.NAtoms = None
|
|
95
|
+
|
|
96
|
+
for line in open(self.path, 'r').readlines():
|
|
97
|
+
|
|
98
|
+
if not job_type and re.search('^ #', line):
|
|
99
|
+
|
|
100
|
+
if "opt" in line:
|
|
101
|
+
if "freq" in line:
|
|
102
|
+
job_type = 'optfreq'
|
|
103
|
+
else:
|
|
104
|
+
job_type = 'opt'
|
|
105
|
+
elif "freq" in line:
|
|
106
|
+
if "opt" in line:
|
|
107
|
+
job_type = 'optfreq'
|
|
108
|
+
else:
|
|
109
|
+
job_type = 'freq'
|
|
110
|
+
flags["freq_flag"] = True
|
|
111
|
+
elif "nmr" in line:
|
|
112
|
+
job_type = 'nmr'
|
|
113
|
+
else:
|
|
114
|
+
job_type = 'sp'
|
|
115
|
+
|
|
116
|
+
if self.NAtoms is None and re.search('^ NAtoms=', line):
|
|
117
|
+
self.NAtoms = int(line.split()[1])
|
|
118
|
+
|
|
119
|
+
if job_type == 'optfreq' or job_type == "freq":
|
|
120
|
+
|
|
121
|
+
if flags['freq_flag'] == False and re.search('Normal termination', line): flags['freq_flag'] = True
|
|
122
|
+
# We skip the opt part of optfreq job, all info is in the freq part
|
|
123
|
+
|
|
124
|
+
if flags['freq_flag'] == True:
|
|
125
|
+
|
|
126
|
+
if re.search('SCF Done', line):
|
|
127
|
+
self.E = float(line.split()[4])
|
|
128
|
+
elif re.search('Sum of electronic and zero-point Energies', line):
|
|
129
|
+
self.Ezpe = float(line.split()[6])
|
|
130
|
+
elif re.search('Sum of electronic and thermal Enthalpies', line):
|
|
131
|
+
self.H = float(line.split()[6])
|
|
132
|
+
elif re.search('Sum of electronic and thermal Free Energies', line):
|
|
133
|
+
self.F = float(line.split()[7])
|
|
134
|
+
|
|
135
|
+
elif re.search('Coordinates', line) and len(geom) == 0:
|
|
136
|
+
flags['read_geom'] = True
|
|
137
|
+
|
|
138
|
+
elif flags['read_geom'] == True and re.search('^\s*.\d', line):
|
|
139
|
+
geom.append([float(x) for x in line.split()[3:6]])
|
|
140
|
+
atoms.append(Mol.element_symbol(line.split()[1]))
|
|
141
|
+
if int(line.split()[0]) == self.NAtoms:
|
|
142
|
+
flags['read_geom'] = False
|
|
143
|
+
|
|
144
|
+
elif re.search(' Deg. of freedom', line):
|
|
145
|
+
self.NVibs = int(line.split()[3])
|
|
146
|
+
|
|
147
|
+
elif re.search('^ Frequencies', line):
|
|
148
|
+
freq_line = line.strip()
|
|
149
|
+
for f in freq_line.split()[2:5]: freq.append(float(f))
|
|
150
|
+
flags['normal_mode'] = False
|
|
151
|
+
|
|
152
|
+
elif re.search('^ IR Inten', line):
|
|
153
|
+
ir_line = line.strip()
|
|
154
|
+
for i in ir_line.split()[3:6]: ints.append(float(i))
|
|
155
|
+
|
|
156
|
+
elif re.search('^ Atom AN', line):
|
|
157
|
+
flags['normal_mode'] = True # locating normal modes of a frequency
|
|
158
|
+
mode_1 = [];
|
|
159
|
+
mode_2 = [];
|
|
160
|
+
mode_3 = [];
|
|
161
|
+
# continue
|
|
162
|
+
|
|
163
|
+
elif flags['normal_mode'] == True and re.search('^\s*\d*\s*.\d*', line) and len(line.split()) > 3:
|
|
164
|
+
mode_1.append([float(x) for x in line.split()[2:5]])
|
|
165
|
+
mode_2.append([float(x) for x in line.split()[5:8]])
|
|
166
|
+
mode_3.append([float(x) for x in line.split()[8:11]])
|
|
167
|
+
|
|
168
|
+
elif flags['normal_mode'] == True:
|
|
169
|
+
flags['normal_mode'] = False
|
|
170
|
+
for m in [mode_1, mode_2, mode_3]: vibs.append(np.array(m))
|
|
171
|
+
|
|
172
|
+
elif job_type == 'opt':
|
|
173
|
+
|
|
174
|
+
if re.search('SCF Done', line): E = float(line.split()[4])
|
|
175
|
+
if re.search('Optimization completed.', line):
|
|
176
|
+
self.E = E;
|
|
177
|
+
flags['opt_flag'] = True
|
|
178
|
+
if flags['opt_flag'] == True:
|
|
179
|
+
if re.search('Standard orientation:', line):
|
|
180
|
+
flags['read_geom'] = True
|
|
181
|
+
|
|
182
|
+
elif flags['read_geom'] == True and re.search('^\s*.\d', line):
|
|
183
|
+
geom.append([float(x) for x in line.split()[3:6]])
|
|
184
|
+
atoms.append(Mol.element_symbol(line.split()[1]))
|
|
185
|
+
if int(line.split()[0]) == self.NAtoms:
|
|
186
|
+
flags['read_geom'] = False
|
|
187
|
+
|
|
188
|
+
elif job_type == 'nmr':
|
|
189
|
+
|
|
190
|
+
if re.search('SCF Done', line):
|
|
191
|
+
self.E = float(line.split()[4])
|
|
192
|
+
elif re.search('Coordinates', line) and len(geom) == 0:
|
|
193
|
+
flags['read_geom'] = True
|
|
194
|
+
|
|
195
|
+
elif flags['read_geom'] == True and re.search('^\s*.\d', line):
|
|
196
|
+
geom.append([float(x) for x in line.split()[3:6]])
|
|
197
|
+
atoms.append(Mol.element_symbol(line.split()[1]))
|
|
198
|
+
if int(line.split()[0]) == self.NAtoms:
|
|
199
|
+
flags['read_geom'] = False
|
|
200
|
+
|
|
201
|
+
elif re.search('Total nuclear spin-spin coupling J', line):
|
|
202
|
+
spin = [[] for i in range(self.NAtoms)]
|
|
203
|
+
flags['jcoup_flag'] = True
|
|
204
|
+
|
|
205
|
+
elif flags['jcoup_flag'] == True and re.search('-?\d\.\d+[Dd][+\-]\d\d?', line):
|
|
206
|
+
for x in line.split()[1:]:
|
|
207
|
+
spin[int(line.split()[0]) - 1].append(float(x.replace('D', 'E')))
|
|
208
|
+
|
|
209
|
+
elif flags['jcoup_flag'] == True and re.search('End of Minotr F.D. properties file', line):
|
|
210
|
+
flags['jcoup_flag'] = False
|
|
211
|
+
|
|
212
|
+
elif job_type == 'sp':
|
|
213
|
+
|
|
214
|
+
if re.search('SCF Done', line):
|
|
215
|
+
self.E = float(line.split()[4])
|
|
216
|
+
elif re.search('Standard orientation:', line):
|
|
217
|
+
flags['read_geom'] = True
|
|
218
|
+
elif flags['read_geom'] == True and re.search('^\s*.\d', line):
|
|
219
|
+
geom.append([float(x) for x in line.split()[3:6]])
|
|
220
|
+
atoms.append(Mol.element_symbol(line.split()[1]))
|
|
221
|
+
if int(line.split()[0]) == self.NAtoms:
|
|
222
|
+
flags['read_geom'] = False
|
|
223
|
+
|
|
224
|
+
# postprocessing:
|
|
225
|
+
if job_type == 'freq' or job_type == 'optfreq':
|
|
226
|
+
self.Freq = np.array(freq)
|
|
227
|
+
self.Ints = np.array(ints)
|
|
228
|
+
self.Vibs = np.zeros((self.NVibs, self.NAtoms, 3))
|
|
229
|
+
for i in range(self.NVibs): self.Vibs[i, :, :] = vibs[i]
|
|
230
|
+
|
|
231
|
+
if job_type == 'nmr':
|
|
232
|
+
for at in spin:
|
|
233
|
+
while len(at) < self.NAtoms: at.append(0)
|
|
234
|
+
self.nmr = np.tril(spin)
|
|
235
|
+
|
|
236
|
+
self.xyz = np.array(geom)
|
|
237
|
+
self.type_list = atoms
|
|
238
|
+
self.get_atoms()
|
|
239
|
+
|
|
240
|
+
def connectivity_matrix(self, distXX=1.65, distXH=1.15):
|
|
241
|
+
|
|
242
|
+
""" Creates a connectivity matrix of the molecule. A connectivity matrix holds the information of which atoms are bonded and to what.
|
|
243
|
+
|
|
244
|
+
:param distXX: The max distance between two atoms (not hydrogen) to be considered a bond
|
|
245
|
+
:param distXH: The max distance between any atom and a hydrogen atom to be considered a bond
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
Nat = self.NAtoms
|
|
249
|
+
self.conn_mat = np.zeros((Nat, Nat))
|
|
250
|
+
|
|
251
|
+
for at1 in self.atoms:
|
|
252
|
+
for at2 in self.atoms:
|
|
253
|
+
|
|
254
|
+
dist = Measure.get_distance([at1,at2])
|
|
255
|
+
|
|
256
|
+
if at1 == at2:
|
|
257
|
+
pass
|
|
258
|
+
|
|
259
|
+
elif (at1.type == 'H' or at2.type == 'H') and dist < distXH:
|
|
260
|
+
self.conn_mat[at1.index, at2.index] = 1;
|
|
261
|
+
self.conn_mat[at2.index, at1.index] = 1
|
|
262
|
+
elif (at1.type != 'H' and at2.type != 'H') and dist < distXX:
|
|
263
|
+
self.conn_mat[at1.index, at2.index] = 1;
|
|
264
|
+
self.conn_mat[at2.index, at1.index] = 1
|
|
265
|
+
|
|
266
|
+
# Remove bifurcated Hs:
|
|
267
|
+
for at1 in self.get_atoms():
|
|
268
|
+
if at1.type == 'H' and np.sum(self.conn_mat[at1.index, :]) > 1:
|
|
269
|
+
|
|
270
|
+
at2list = np.where(self.conn_mat[at1.index, :] == 1)
|
|
271
|
+
at2list = at2list[0].tolist()
|
|
272
|
+
|
|
273
|
+
at2dist = [round(Measure.get_distance([at1, self.atoms[at2x]]), 3) for at2x in at2list]
|
|
274
|
+
for at, dist in zip(at2list, at2dist):
|
|
275
|
+
if self.atoms[at].type == 'H':
|
|
276
|
+
at2list.remove(at)
|
|
277
|
+
at2dist.remove(dist)
|
|
278
|
+
|
|
279
|
+
at2 = at2list[at2dist.index(min(at2dist))]
|
|
280
|
+
for at2x in at2list:
|
|
281
|
+
if at2x != at2:
|
|
282
|
+
print('remove', self._id, at2x, at1, at2)
|
|
283
|
+
self.conn_mat[at1.index, at2x] = 0;
|
|
284
|
+
self.conn_mat[at2x, at1.index] = 0
|
|
285
|
+
|
|
286
|
+
graph = nx.graph.Graph(self.conn_mat)
|
|
287
|
+
self.Nmols = nx.number_connected_components(graph)
|
|
288
|
+
self.graph = graph
|
|
289
|
+
|
|
290
|
+
def cp2k(self):
|
|
291
|
+
|
|
292
|
+
""" Parses information from CP2K. Right now only works for AIMD Trajectories, plan to implement opt compatability later.
|
|
293
|
+
|
|
294
|
+
:param job_type: (string) Currently only md is supported. Should be manually specified.
|
|
295
|
+
:param colvar: (list) A list containing the atom numbers that define your collective variable.
|
|
296
|
+
:param timestep: (float) Timestep of your MD calculation. The function will attempt to extract this from your input file if present.
|
|
297
|
+
:param traj_file: (string) File containing the CP2K trajectory.
|
|
298
|
+
:param input_file: (string) File containing your CP2K inputs.
|
|
299
|
+
:param output_file: (string) File containing your CP2K output.
|
|
300
|
+
"""
|
|
301
|
+
|
|
302
|
+
with open(self.path, 'r') as geom:
|
|
303
|
+
first_line = geom.readline()
|
|
304
|
+
NAtoms = int(first_line.split()[0])
|
|
305
|
+
|
|
306
|
+
raw_coords = geom.readlines()[-NAtoms:]
|
|
307
|
+
|
|
308
|
+
coords = np.array([line.split()[1:] for line in raw_coords], dtype=float)
|
|
309
|
+
types = np.array([line.split()[0] for line in raw_coords])
|
|
310
|
+
|
|
311
|
+
geom.close()
|
|
312
|
+
|
|
313
|
+
self.NAtoms = NAtoms
|
|
314
|
+
self.xyz = coords
|
|
315
|
+
self.type_list = types
|
|
316
|
+
|
|
317
|
+
self.get_atoms()
|
|
318
|
+
|
|
319
|
+
def orca(self, output_file="input.log", job_type="opt"):
|
|
320
|
+
|
|
321
|
+
""" Parses information from ORCA input.log file
|
|
322
|
+
:param output_file: (string) File containing ORCA output file.
|
|
323
|
+
:param job_type: (string) The type of calculation ORCA run.
|
|
324
|
+
"""
|
|
325
|
+
|
|
326
|
+
with open("/".join([self.path, output_file]), "r") as f:
|
|
327
|
+
for line in f:
|
|
328
|
+
if "Geometry Optimization Run" in line:
|
|
329
|
+
output_file = "input.orca.xyz"
|
|
330
|
+
|
|
331
|
+
with open("/".join([self.path, output_file]), "r") as f2:
|
|
332
|
+
first_line = f2.readline()
|
|
333
|
+
Natoms = int(first_line.split()[0])
|
|
334
|
+
|
|
335
|
+
second_line = f2.readlines()[0:1]
|
|
336
|
+
energy = np.array([i.split()[-1] for i in second_line], dtype=float)
|
|
337
|
+
|
|
338
|
+
raw_coords = f2.readlines()[-Natoms:]
|
|
339
|
+
|
|
340
|
+
coords = np.array([line.split()[1:] for line in raw_coords], dtype=float)
|
|
341
|
+
atoms = np.array([line.split()[0] for line in raw_coords])
|
|
342
|
+
|
|
343
|
+
self.Natoms = Natoms
|
|
344
|
+
self.xyz = coords
|
|
345
|
+
self.type_list = atoms
|
|
346
|
+
self.energy = energy
|
|
347
|
+
self.get_atoms()
|
|
348
|
+
|
|
349
|
+
elif "Single Point Calculation" and "FINAL SINGLE POINT ENERGY" in line:
|
|
350
|
+
# print(line.split()[-1])
|
|
351
|
+
job_type = "sp"
|
|
352
|
+
energy = np.array(line.split()[-1], dtype=float)
|
|
353
|
+
print(self.energy)
|
|
354
|
+
|
|
355
|
+
else:
|
|
356
|
+
print("Unable to read ORCA log file")
|
|
357
|
+
|
|
358
|
+
class Data ():
|
|
359
|
+
|
|
360
|
+
def __init__(self):
|
|
361
|
+
|
|
362
|
+
self.data = None
|
|
363
|
+
|
|
364
|
+
def csv(self, skiprows=1, dtype=float, delimiter: str = ','):
|
|
365
|
+
"""
|
|
366
|
+
Parses a csv file and appends the data to the mol object
|
|
367
|
+
|
|
368
|
+
:param file: (string) File containing the csv data.
|
|
369
|
+
:param skiprows: (int) Number of rows to skip
|
|
370
|
+
:param dtype: (type) Data type
|
|
371
|
+
:param delimiter: (str) Delimiter
|
|
372
|
+
"""
|
|
373
|
+
|
|
374
|
+
self.data = np.loadtxt(self.path, skiprows=skiprows, dtype=dtype, delimiter=delimiter)
|
|
375
|
+
|
|
376
|
+
def gromacs(self):
|
|
377
|
+
""" Parses information from gromacs *.xvg file
|
|
378
|
+
|
|
379
|
+
:param file: (string) File containing the gromacs data.
|
|
380
|
+
"""
|
|
381
|
+
|
|
382
|
+
# I don't use Gromacs enough to know how the output looks like. Right now, I am just reading the .xvg file:
|
|
383
|
+
|
|
384
|
+
if re.search('.xvg', self.path):
|
|
385
|
+
|
|
386
|
+
with open(self.path, 'r') as f:
|
|
387
|
+
i = 0
|
|
388
|
+
for line in f.readlines():
|
|
389
|
+
if re.search('#', line) or re.search('@', line):
|
|
390
|
+
i = i + 1
|
|
391
|
+
|
|
392
|
+
if re.search('Time', line):
|
|
393
|
+
time_unit = line.split()[-1].strip('()"')
|
|
394
|
+
self.time_unit = time_unit
|
|
395
|
+
f.close()
|
|
396
|
+
|
|
397
|
+
data = np.loadtxt(self.path, skiprows=i)
|
|
398
|
+
|
|
399
|
+
self.data = data
|
|
400
|
+
self.software = 'gromacs'
|
|
401
|
+
|
|
402
|
+
if re.search('.xpm', self.path):
|
|
403
|
+
|
|
404
|
+
f = open(self.path, 'r')
|
|
405
|
+
pattern = None;
|
|
406
|
+
matrix_lett = [];
|
|
407
|
+
matrix_dict = {}
|
|
408
|
+
grid = None
|
|
409
|
+
for line in f.readlines():
|
|
410
|
+
|
|
411
|
+
if re.search(r'(\d\s\d\b)', line) and grid is None:
|
|
412
|
+
grid = int(line.split()[1])
|
|
413
|
+
pattern = grid * '[A-Z]'
|
|
414
|
+
|
|
415
|
+
if pattern and re.search(pattern, line):
|
|
416
|
+
replL1 = line.replace('",', '')
|
|
417
|
+
replL2 = line.replace('"', '')
|
|
418
|
+
matrix_lett.append('\n'.join(replL2.split()))
|
|
419
|
+
|
|
420
|
+
if re.search('((".*")[0-9])', line):
|
|
421
|
+
replL1 = line.replace('"', ' ')
|
|
422
|
+
letters, energy = values = str(replL1.split()[0]), float(replL1.split()[4])
|
|
423
|
+
matrix_dict.update(dict.fromkeys(letters, energy))
|
|
424
|
+
|
|
425
|
+
matrix = np.zeros([grid, grid])
|
|
426
|
+
for i in range(grid):
|
|
427
|
+
for j in range(grid):
|
|
428
|
+
matrix[grid - i - 1, j] = matrix_dict[matrix_lett[i][j]]
|
|
429
|
+
|
|
430
|
+
self.data = matrix
|
|
431
|
+
self.software = 'gromacs'
|
|
432
|
+
|
|
433
|
+
def cp2k(self, colvar = None, timestep = None):
|
|
434
|
+
|
|
435
|
+
self.time_unit = 'fs' # Default unit in CP2K
|
|
436
|
+
self.software = 'cp2k'
|
|
437
|
+
|
|
438
|
+
if re.search('.xyz', self.path):
|
|
439
|
+
# Reads colvars straight from the xyz file. Requires specification of timestep and colvar coordinate.
|
|
440
|
+
|
|
441
|
+
if colvar == None:
|
|
442
|
+
raise ValueError('Error: Specify the atoms for your collective variables.')
|
|
443
|
+
|
|
444
|
+
if timestep == None:
|
|
445
|
+
raise ValueError('Error: Specify timestep for reading CP2K trajectories.')
|
|
446
|
+
|
|
447
|
+
xyz_array = []
|
|
448
|
+
|
|
449
|
+
with open(self.path, 'r') as geom:
|
|
450
|
+
line_1 = geom.readline()
|
|
451
|
+
self.Natoms = int(line_1.split()[0])
|
|
452
|
+
|
|
453
|
+
for line in geom.readlines():
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
colvar_data = Measure.measure(self.path, colvar)
|
|
458
|
+
time = [i / (1 / timestep) for i in range(len(colvar_data))]
|
|
459
|
+
|
|
460
|
+
data = np.array(list(zip(time, colvar_data)))
|
|
461
|
+
self.data = data
|
|
462
|
+
|
|
463
|
+
elif re.search('.metadynLog', self.path):
|
|
464
|
+
# Reads colvars from the metadynLog file.
|
|
465
|
+
|
|
466
|
+
data = np.loadtxt(self.path)
|
|
467
|
+
self.data = data
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import math
|
|
5
|
+
|
|
6
|
+
class Measure():
|
|
7
|
+
|
|
8
|
+
@staticmethod
|
|
9
|
+
def get_distance(atom_list):
|
|
10
|
+
|
|
11
|
+
at1 = atom_list[0]
|
|
12
|
+
at2 = atom_list[1]
|
|
13
|
+
|
|
14
|
+
dist = 0
|
|
15
|
+
for dim in range(2):
|
|
16
|
+
dist += (at1.coord[dim]-at2.coord[dim])**2
|
|
17
|
+
|
|
18
|
+
return math.sqrt(dist)
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def get_angle(atom_list):
|
|
22
|
+
|
|
23
|
+
at1 = atom_list[0]
|
|
24
|
+
at2 = atom_list[1]
|
|
25
|
+
at3 = atom_list[2]
|
|
26
|
+
|
|
27
|
+
r1 = [x-y for x,y in zip(at1.coord,at2.coord)]
|
|
28
|
+
r2 = [x-y for x,y in zip(at3.coord,at3.coord)]
|
|
29
|
+
|
|
30
|
+
norm1 = Measure.get_distance([at1,at2])
|
|
31
|
+
norm2 = Measure.get_distance([at2,at3])
|
|
32
|
+
|
|
33
|
+
norm = norm1*norm2
|
|
34
|
+
dot = np.dot(r1,r2)/norm
|
|
35
|
+
angle = math.acos(dot)
|
|
36
|
+
axor = np.cross(r1,r2)
|
|
37
|
+
|
|
38
|
+
return angle*180/np.pi, axor
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def get_dihedral(atom_list):
|
|
42
|
+
|
|
43
|
+
at1 = atom_list[0]
|
|
44
|
+
at2 = atom_list[1]
|
|
45
|
+
at3 = atom_list[2]
|
|
46
|
+
at4 = atom_list[3]
|
|
47
|
+
|
|
48
|
+
r1 = [x-y for x,y in zip(at1.coord,at2.coord)]
|
|
49
|
+
r2 = [x-y for x,y in zip(at2.coord,at3.coord)]
|
|
50
|
+
r3 = [x-y for x,y in zip(at3.coord,at4.coord)]
|
|
51
|
+
|
|
52
|
+
p1 = np.cross(r1,r2)
|
|
53
|
+
p2 = np.cross(r2,r3)
|
|
54
|
+
|
|
55
|
+
axor = np.cross(p1,p2)
|
|
56
|
+
|
|
57
|
+
norm_p1 = np.sqrt(np.sum(p1**2))
|
|
58
|
+
norm_p2 = np.sqrt(np.sum(p2**2))
|
|
59
|
+
|
|
60
|
+
norm = norm_p1*norm_p2
|
|
61
|
+
dot = np.clip(np.dot(p1,p2)/norm,-0.999999999,0.999999999)
|
|
62
|
+
angle = math.acos(dot)
|
|
63
|
+
|
|
64
|
+
ppoint = -np.dot(p1,at1.coord)
|
|
65
|
+
dpoint = np.dot(p1,at4.coord) + ppoint/norm_p1
|
|
66
|
+
|
|
67
|
+
if dpoint >= 0:
|
|
68
|
+
return -(angle*180/np.pi)
|
|
69
|
+
else:
|
|
70
|
+
return angle*180/np.pi
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def measure(atom_list):
|
|
74
|
+
|
|
75
|
+
if len(atom_list) == 2:
|
|
76
|
+
return Measure.get_distance(atom_list)
|
|
77
|
+
elif len(atom_list) == 3:
|
|
78
|
+
return Measure.get_angle(atom_list)
|
|
79
|
+
elif len(atom_list) == 4:
|
|
80
|
+
return Measure.get_dihedral(atom_list)
|
matmalib-0.0.1/PKG-INFO
ADDED
matmalib-0.0.1/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# matmalib
|