-
Notifications
You must be signed in to change notification settings - Fork 55
/
PyLAT.py
346 lines (282 loc) · 17.7 KB
/
PyLAT.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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
#!/usr/bin/env python
"""
Created on Wed May 20 13:37:57 2015
@author: mhumbert
PyLAT: Python LAMMPS Analysis Tools
Copyright (C) 2018 Michael Humbert, Yong Zhang and Ed Maginn
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
This is the driver file for the PyLAT program. This file parsers the user
input, and then calls the proper class files
"""
if __name__ == '__main__':
import argparse as args
import sys
import json
try:
import src.calccomf
except ImportError:
print("Please use either 'sh compile.sh' or 'python compile.py' to compile the fortran modules")
sys.exit(1)
#sys.path.append('~/Projects/Parser/publish/src')
from src.MSD import MSD
from src.calcDiffusivity import calcdiffusivity
from src.calcCOM import calcCOM
from src.getTimeData import gettimedata
from src.getMolData import getmoldata
from src.COMradial import COMradialdistribution
from src.getAtomCharges import getatomcharges
from src.calcNEconductivity import calcNEconductivity
from src.calcCond import calcCond
from src.getCoordinationNumber import getcoordinationnumber
from src.ionpair import ionpair
from src.calcDielectricConstant import calcDielectricConstant
from src.distSearch import distSearch
from src.calcVisc import calcVisc
from src.fitVisc import fitVisc
parser = args.ArgumentParser(formatter_class=args.RawTextHelpFormatter, description='''
Analyze output from LAMMPS simulation
Required files are the log file, the data file and at least one trajectory
file in that order
Also required is the name and number of each molecule type in the system.
To do this, use the --mol and --nummol flags. Use the same order as they
were entered into the data file.
If you want to calculate an ionic conductivity or dielectric constant, a
temperature must be included using a --temp flag.
The output of this program is a json file with all of the requested
information. Default file name is output.json. To use data in this file,
use the following python code:
import json
openfile = open("filename")
dict = json.load(openfile)
This yields a dictionary with all of the information that you can query.
plot.py is an interactive program written to parse and display the
information in the json output files. Proper usage is
plots.py {output file}
''', epilog = '''
Example: For diffusivity and RDF of a system with 500 water and 500
methanol in the directory methanol/inwater
python PyLAT.py -d -g --mol H2O --mol CH3OH --nummol 500
--nummol 500 -p methanol/inwater/ -f methanolwater.json -v 2 mol.log
restart.dat mol1.lammpstrj mol2.lammpstrj
''')
File_Options = parser.add_argument_group("File arguments")
File_Options.add_argument('LOG', nargs=1, help = 'LAMMPS log file.')
File_Options.add_argument('DAT', nargs=1, help = 'LAMMPS data dile.')
File_Options.add_argument('TRJ', action='append', nargs='+',
help = 'LAMMPS trajectory file. Can include multiple trajectory files.')
File_Options.add_argument('-p', '--path',
help = 'Path to directory containing files. Will also save output in same directory. Default is current directory. Example: path/to/directory/',
default = './')
File_Options.add_argument('-i', '--input', help = 'json file if properties have already been calculated for this system')
File_Options.add_argument('-f', '--output',
help = 'File name for output file. Defult file name is output.json', default = 'output.json')
properties = parser.add_argument_group("Possible Properties")
properties.add_argument('-g', '--RDF', help = 'Radial Distribution Function.', action = 'store_true')
properties.add_argument('-c', '--coord', help = 'Coordination Number. Will determine the first three minima of the RDF and calculates the coordination numbers integrating to those points.', action = 'store_true')
properties.add_argument('-m', '--MSD', help = 'Mean Square Displacement.', action = 'store_true')
properties.add_argument('-d', '--D',
help = 'Diffusivity using MSD. Automatically includes MSD calculations.',
action = 'store_true')
properties.add_argument('--NEC',
help = 'Nernst Einstein ionic conductivity. Automatically includes MSD and diffusivity calculations.',
action = 'store_true')
properties.add_argument('--GKC', help = 'Green-Kubo ionic conductivity.', action = 'store_true')
properties.add_argument('--IPL', help = 'Ion Pair Lifetime.', action = 'store_true')
properties.add_argument('--DEC', help = 'Dielectric Constant', action = 'store_true')
properties.add_argument('--DS', help = 'Search for molecules of given types at a given distance', action = 'store_true')
properties.add_argument('--Visc',help='Calculate the viscosity of the system. Requires multiple log files in different directories. The name of the log files will be taken from the LOG option', action='store_true')
system = parser.add_argument_group("System Properties")
system.add_argument('--mol', help = 'Add molecule name to list of molecules. Required', action = 'append', required=True)
system.add_argument('--nummol', help = 'Add number of molecule type to list. Must have same number as --mol. Required',
action = 'append', required=True, type=int)
system.add_argument('-T', '--temp', help = 'Temperature of system. Required for conductivity calculations', type=float)
parser.add_argument('-v', '--verbose', help = '''Verbosity of program: \n0: No print statements \n1: Print after each major step \n2: Print after each major step and progress of the major step at each minor step. Not recommended for cluster calculations''', type=int)
MSDoptions = parser.add_argument_group('MSD Options')
MSDoptions.add_argument('--MSD_skip', help = 'Number of timesteps to skip at the beginning of the trajectory file before calculating MSD. Default is 0', default = 0, type=int)
MSDoptions.add_argument('--MSD_num_init', help = 'Number of initial timesteps to consider in MSD calculation. Default is half of frames being used in MSD calculation', default = None)
Difoptions = parser.add_argument_group('Diffusivity Options')
Difoptions.add_argument('--D_Tolerance', help = 'Tolerance for determining linear region in Log-Log plot. Default is 0.075', type=float, default=0.075)
RDFoptions = parser.add_argument_group('RDF Options')
RDFoptions.add_argument('--RDF_Timesteps', help = 'Number of timesteps to use in the RDF calculation. \nWill use the last n timesteps. \nDefault is to use all timesteps', type = int)
RDFoptions.add_argument('--RDF_maxr', help = 'Maximum r for RDF calculation. Default is half the shortest box length.', default = None)
RDFoptions.add_argument('--RDF_binsize', help = 'Bin size for RDF calculation. Default is 0.1', default = 0.1, type = float)
GKCoptions = parser.add_argument_group('GK Conductivity options')
GKCoptions.add_argument('--GKC_skip', help = 'Number of timesteps to skip at the beginning of the trajectory file before calculating GK Conductivity. Default is 0', default = 0, type=int)
GKCoptions.add_argument('--GKC_Tolerance', help= 'Tolerance for finding the converged region for GK conductivity calculation. Default is 0.001', default = 0.001, type=float)
GKCoptions.add_argument('--GKC_J_Output', help='Option to output the charge flux correlation function. If included, a file J.dat will be created with the values of the correlation function.', action='store_true')
IPLoptions = parser.add_argument_group('Ion Pair Lifetime options')
IPLoptions.add_argument('--IPL_skip', help = 'Number of timesteps to skip at the beginning of the trajectory file before calculating Ion Pair Lifetime. Default is 0', default = 0, type=int)
DECoptions = parser.add_argument_group('Dielectric Constant options')
DECoptions.add_argument('--DEC_start', help = 'Starting frame for dielectric constant calculations. Default is 1', default = 1, type = int)
DSoptions = parser.add_argument_group('Distance Search Options')
DSoptions.add_argument('--DS_MOL1', help = 'Molecule name for the first molecule type. Required for Distance Search')
DSoptions.add_argument('--DS_MOL2', help = 'Molecule name for the second molecule type. Required for Distance Search')
DSoptions.add_argument('--DS_Dist', help = 'Distance between molecules. Required for Distance Search', type = float)
DSoptions.add_argument('--DS_Dist_Tol', help = 'Tolerance for distance. Required for Distance Search', type = float)
DSoptions.add_argument('--DS_First_Frame', help = 'First frame for distance search calculation. Default is 1', default = 1, type = int)
DSoptions.add_argument('--DS_Num_Samples', help = 'Number of samples to find. Default is 1', default = 1, type = int)
Viscoptions = parser.add_argument_group('Viscosity options')
Viscoptions.add_argument('--Visc_Dir', help = 'Basename for the viscosity directories. For example, if basename is "visc" the directories should be visc1, visc2, ... \n If no base name is given, directories should be 1, 2, 3, ...',
default = None)
Viscoptions.add_argument('--Visc_Num', help = 'Number of Trajectories to use in the viscosity calculation. Each trajectory should have a seperate trajectory',type=int)
Viscoptions.add_argument('--Visc_Skip', help = 'Number of timesteps to skip in the viscosity calculation. Default of 0 should only be used if the simulations were equilibrated before the start of the production run', default=0, type=int)
Viscoptions.add_argument('--Visc_Num_Boots', help = 'Number of times to sample trajectories for bootstrapping. Default is 10', default = 10, type = int)
Viscoptions.add_argument('--Visc_Samples', help = 'Number of samples for each bootstrapping iteration. Default is 30', default = 30, type = int)
Viscoptions.add_argument('--Visc_Plot', help = 'Include plotting for viscosity fitting. Matplotlib required for fitting. Will output to x11', action = 'store_true')
Viscoptions.add_argument('--Visc_Guess', help = 'Initial guess for viscosity fitting parameters. Must add all four parameters with one call. Order of parameters is A, alpha, tau1, tau2. Default is the equivalent of calling "--Visc_Guess 1e-3 1.5e-1 1e2 1e3".', type = float, nargs = 4)
arg = parser.parse_args()
c = calcCOM()
m = MSD()
cd = calcdiffusivity()
gt = gettimedata()
gm = getmoldata()
crd = COMradialdistribution()
gc = getatomcharges()
ne = calcNEconductivity()
cc = calcCond()
gcn = getcoordinationnumber()
ip = ionpair()
dec = calcDielectricConstant()
ds = distSearch()
cv = calcVisc()
fv = fitVisc()
if arg.path[-1] != '/':
arg.path = arg.path + '/'
for i in range(0,len(arg.TRJ[0])):
arg.TRJ[0][i] = arg.path + arg.TRJ[0][i]
trjfilename = arg.TRJ[0]
datfilename = arg.path + arg.DAT[0]
logfilename = arg.path + arg.LOG[0]
if arg.verbose == 2:
ver = True
else:
ver = False
if arg.input == None:
output = {}
else:
inputfile = arg.path + arg.input
openfile = open(inputfile, 'r')
output = json.load(openfile)
openfile.close()
nummoltype = arg.nummol
moltypel = arg.mol
moltype = []
for i in range(0,len(moltypel)):
for j in range(0,nummoltype[i]):
moltype.append(int(i))
if arg.NEC or arg.GKC:
if arg.temp == None:
sys.exit('Need temperature for conductivity calculations')
if 'Conductivity' not in output.keys():
output['Conductivity'] = {}
output['Conductivity']['units'] = 'S/m'
if arg.DEC:
if arg.temp == None:
sys.exit('Need temperature for dielectric constant calculations')
if len(arg.mol) != len(arg.nummol):
sys.exit('Must enter same number of arguments for nummol and mol')
if arg.NEC and 'Diffusivity' not in output.keys():
arg.D = True
if arg.D and 'MSD' not in output.keys():
arg.MSD = True
if arg.coord and 'RDF' not in output.keys():
arg.RDF = True
if arg.MSD or arg.D or arg.IPL:
tsjump = gt.getjump(trjfilename[0])
dt = gt.getdt(logfilename)
if arg.NEC or arg.GKC or arg.GKC or arg.DEC:
n = gc.findnumatoms(datfilename)
(molcharges, atomcharges,n) = gc.getmolcharges(datfilename,n)
molcharge = gc.molchargedict(molcharges, moltypel, moltype)
if arg.RDF or arg.MSD or arg.IPL or arg.coord or arg.DEC:
(V, Lx, Ly, Lz) = gcn.getvolume(trjfilename[0])
if arg.RDF or arg.MSD or arg.IPL:
if arg.verbose >= 1:
print('beginning COM calculation')
(comx, comy, comz, Lx, Ly, Lz, Lx2, Ly2, Lz2) = c.calcCOM(trjfilename,datfilename, ver)
if arg.verbose == 1:
print('COM calculation complete')
if arg.RDF:
if arg.RDF_Timesteps == None:
arg.RDF_Timesteps = len(comx)
if arg.RDF_Timesteps > len(comx):
print('Number of RDF timesteps requested longer than simulation. Using all timesteps.')
arg.RDF_Timesteps = len(comx)
if arg.verbose >= 1:
print('beginning RDF calculation')
output['RDF'] = {}
output['RDF']['units'] = 'unitless, angstroms'
output = crd.runradial(datfilename, comx, comy, comz, Lx, Ly, Lz, Lx2, Ly2, Lz2, output, nummoltype, moltypel, moltype, arg.RDF_Timesteps, ver,arg.RDF_maxr,arg.RDF_binsize)
if arg.verbose >= 1:
print('RDF calculation complete')
if arg.coord:
if arg.verbose >= 1:
print('beginning coordination number calculation')
output = gcn.calccoordinationnumber(output, nummoltype, moltypel, V)
if arg.verbose >= 1:
print('coordination number calculation complete')
if arg.DEC:
output = dec.calcDEC(atomcharges, trjfilename, arg.temp, output, V, arg.verbose, arg.DEC_start)
if arg.GKC:
if arg.verbose >= 1:
print('beginning GK conductivity calculation')
output = cc.calcConductivity(molcharges, trjfilename, logfilename, datfilename, arg.temp, output, moltype, moltypel,arg.verbose,arg.GKC_skip,arg.GKC_Tolerance,arg.GKC_J_Output)
if arg.verbose >= 1:
print('GK conductivity calculation complete')
if arg.IPL:
if arg.verbose >= 1:
print('beginning Ion Pair Lifetime calculation')
ip.runionpair(comx,comy,comz,Lx,Ly,Lz,moltypel,moltype,tsjump,dt,output,ver,arg.IPL_skip)
if arg.verbose >= 1:
print('Ion Pair Lifetime calculation complete')
if arg.DS:
if arg.verbose >= 1:
print('beginning Distance Search')
output = ds.distSearch(arg.DS_MOL1, arg.DS_MOL2, arg.DS_Dist, arg.DS_Dist_Tol, arg.DS_First_Frame, arg.DS_Num_Samples, trjfilename, datfilename, moltype, moltypel, output)
if arg.MSD:
if arg.verbose >= 1:
print('beginning MSD calculation')
output = m.runMSD(comx, comy, comz, Lx, Ly, Lz, Lx2, Ly2, Lz2, moltype, moltypel, dt, tsjump, output, ver, arg.MSD_skip, arg.MSD_num_init)
if arg.verbose == 1:
print('MSD calculation complete')
if arg.D:
if arg.verbose >= 1:
print('beginning diffusivity calculation')
cd.calcdiffusivity(output, moltypel, dt,arg.D_Tolerance)
if arg.verbose:
print('diffusivity calculation complete')
if arg.NEC:
if arg.verbose >= 1:
print('beginning NE conductivity calculation')
output = ne.calcNEconductivity(output, molcharge, Lx, Ly, Lz, nummoltype, moltypel, arg.temp)
if arg.verbose >= 1:
print('NE conductivity calculation complete')
if arg.Visc:
if arg.verbose >= 1:
print('beginning viscosity calculation')
if arg.Visc_Guess == None:
arg.Visc_Guess = [1e-3,1.5e-1,1e2,1e3]
if len(arg.Visc_Guess) != 4:
print('need four inputs for viscosity initial parameters.')
output = cv.calcvisc(arg.Visc_Num,arg.Visc_Skip,arg.Visc_Dir,arg.LOG[0],output, arg.verbose, arg.Visc_Samples, arg.Visc_Num_Boots, arg.Visc_Plot, arg.Visc_Guess)
#output = fv.fitvisc(output)
if arg.verbose >= 1:
print('viscosity calculation complete')
if output == {}:
sys.exit('Please select at least one property to calculate')
if arg.verbose >= 1:
print('beginning file generation')
outputfile = open(arg.path + arg.output, 'w')
json.dump(output, outputfile, indent=4, sort_keys=True)
outputfile.close()
if arg.verbose >= 1:
print('file generated')