bfee2 3.1.1.post1__py3-none-any.whl

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.
Files changed (68) hide show
  1. BFEE2/__init__.py +0 -0
  2. BFEE2/commonTools/__init__.py +0 -0
  3. BFEE2/commonTools/commonSlots.py +48 -0
  4. BFEE2/commonTools/fileParser.py +327 -0
  5. BFEE2/commonTools/ploter.py +218 -0
  6. BFEE2/doc/Doc.pdf +0 -0
  7. BFEE2/doc/__init__.py +1 -0
  8. BFEE2/gui.py +2785 -0
  9. BFEE2/inputGenerator.py +2949 -0
  10. BFEE2/postTreatment.py +676 -0
  11. BFEE2/templates_gromacs/000.colvars.template +37 -0
  12. BFEE2/templates_gromacs/000.generate_tpr_sh.template +31 -0
  13. BFEE2/templates_gromacs/000.mdp.template +74 -0
  14. BFEE2/templates_gromacs/001.colvars.template +76 -0
  15. BFEE2/templates_gromacs/001.generate_tpr_sh.template +31 -0
  16. BFEE2/templates_gromacs/001.mdp.template +73 -0
  17. BFEE2/templates_gromacs/001.readme.template +1 -0
  18. BFEE2/templates_gromacs/002.colvars.template +101 -0
  19. BFEE2/templates_gromacs/002.generate_tpr_sh.template +31 -0
  20. BFEE2/templates_gromacs/002.mdp.template +73 -0
  21. BFEE2/templates_gromacs/003.colvars.template +125 -0
  22. BFEE2/templates_gromacs/003.generate_tpr_sh.template +36 -0
  23. BFEE2/templates_gromacs/003.mdp.template +73 -0
  24. BFEE2/templates_gromacs/004.colvars.template +148 -0
  25. BFEE2/templates_gromacs/004.generate_tpr_sh.template +37 -0
  26. BFEE2/templates_gromacs/004.mdp.template +74 -0
  27. BFEE2/templates_gromacs/005.colvars.template +170 -0
  28. BFEE2/templates_gromacs/005.generate_tpr_sh.template +38 -0
  29. BFEE2/templates_gromacs/005.mdp.template +74 -0
  30. BFEE2/templates_gromacs/006.colvars.template +192 -0
  31. BFEE2/templates_gromacs/006.generate_tpr_sh.template +39 -0
  32. BFEE2/templates_gromacs/006.mdp.template +74 -0
  33. BFEE2/templates_gromacs/007.colvars.template +210 -0
  34. BFEE2/templates_gromacs/007.generate_tpr_sh.template +40 -0
  35. BFEE2/templates_gromacs/007.mdp.template +73 -0
  36. BFEE2/templates_gromacs/007_eq.colvars.template +169 -0
  37. BFEE2/templates_gromacs/007_eq.generate_tpr_sh.template +64 -0
  38. BFEE2/templates_gromacs/007_min.mdp.template +62 -0
  39. BFEE2/templates_gromacs/008.colvars.template +42 -0
  40. BFEE2/templates_gromacs/008.generate_tpr_sh.template +31 -0
  41. BFEE2/templates_gromacs/008.mdp.template +74 -0
  42. BFEE2/templates_gromacs/008_eq.colvars.template +14 -0
  43. BFEE2/templates_gromacs/008_eq.generate_tpr_sh.template +31 -0
  44. BFEE2/templates_gromacs/BFEEGromacs.py +1268 -0
  45. BFEE2/templates_gromacs/__init__.py +0 -0
  46. BFEE2/templates_gromacs/find_min_max.awk +27 -0
  47. BFEE2/templates_namd/__init__.py +0 -0
  48. BFEE2/templates_namd/configTemplate.py +1152 -0
  49. BFEE2/templates_namd/fep.tcl +299 -0
  50. BFEE2/templates_namd/fep_lddm.tcl +312 -0
  51. BFEE2/templates_namd/scriptTemplate.py +304 -0
  52. BFEE2/templates_namd/solvate.tcl +9 -0
  53. BFEE2/templates_namd/solvate_mem.tcl +9 -0
  54. BFEE2/templates_namd/updateCenters.py +312 -0
  55. BFEE2/templates_readme/Readme_Gromacs_Geometrical.txt +25 -0
  56. BFEE2/templates_readme/Readme_NAMD_Alchemical.txt +20 -0
  57. BFEE2/templates_readme/Readme_NAMD_Geometrical.txt +34 -0
  58. BFEE2/templates_readme/__init__.py +1 -0
  59. BFEE2/templates_readme/rags.py +187 -0
  60. BFEE2/third_party/__init__.py +0 -0
  61. BFEE2/third_party/py_bar.py +585 -0
  62. BFEE2/version.py +4 -0
  63. bfee2-3.1.1.post1.data/scripts/BFEE2Gui.py +19 -0
  64. bfee2-3.1.1.post1.dist-info/METADATA +86 -0
  65. bfee2-3.1.1.post1.dist-info/RECORD +68 -0
  66. bfee2-3.1.1.post1.dist-info/WHEEL +5 -0
  67. bfee2-3.1.1.post1.dist-info/licenses/LICENSE +677 -0
  68. bfee2-3.1.1.post1.dist-info/top_level.txt +1 -0
BFEE2/__init__.py ADDED
File without changes
File without changes
@@ -0,0 +1,48 @@
1
+ # simple and general slots called by gui.py
2
+
3
+ from PySide6.QtWidgets import QLineEdit, QFileDialog, QMessageBox
4
+
5
+ def openFileDialog(fileType, lineEdit):
6
+ """return a openFile function that opens special type of files
7
+ the openFile dialog is connected with a lineEdit widget
8
+
9
+ Args:
10
+ fileType (str): the file type for opening
11
+ lineEdit (QLineEdit): the QLineEdit that connects to the QFileDialog
12
+
13
+ Returns:
14
+ function obj: slot function opening special type of files
15
+ """
16
+
17
+ def openFile():
18
+ fileName, _ = QFileDialog.getOpenFileName(
19
+ None,
20
+ f'Choose {fileType} file',
21
+ '',
22
+ f'All Files (*)'
23
+ )
24
+ lineEdit.setText(fileName)
25
+ return openFile
26
+
27
+ def openFilesDialog(fileType, listWidget):
28
+ """return a openFile function that opens a series of files
29
+ the openFile dialog is connected with a QListWidget
30
+
31
+ Args:
32
+ fileType (str): the file type for opening
33
+ listWidget (QListWidget): the QListWidget that connects to the QFileDialog
34
+
35
+ Returns:
36
+ function obj: slot function opening a series of files
37
+ """
38
+
39
+ def openFiles():
40
+
41
+ fileNames, _ = QFileDialog.getOpenFileNames(
42
+ None,
43
+ f'Choose {fileType} files',
44
+ '',
45
+ f'All Files (*)'
46
+ )
47
+ listWidget.addItems(fileNames)
48
+ return openFiles
@@ -0,0 +1,327 @@
1
+ # file parser used in bfee
2
+
3
+ import math
4
+ import os
5
+ import shutil
6
+ import sys
7
+
8
+ import MDAnalysis
9
+ import numpy as np
10
+ import parmed
11
+ from MDAnalysis import transformations
12
+
13
+
14
+ # an runtime error
15
+ # selection corresponding to nothing
16
+ class SelectionError(RuntimeError):
17
+ def __init__(self, arg):
18
+ self.args = arg
19
+
20
+ class fileParser:
21
+ """topology and coordinate parser,
22
+ this class implements basic method for the topology and
23
+ coordinate file, used by BFEE
24
+ """
25
+
26
+ def __init__(self, topFile, coorFile=''):
27
+ """initialize fileParser, on coorFile is not provided, then
28
+ topFile is considered as a top-coor file, such as mol2
29
+
30
+ Args:
31
+ topFile (str): path of the topology (psf, parm) file
32
+ coorFile (str): path of the coordinate (pdb, rst) file. Defaults to ''.
33
+ """
34
+
35
+ coorPostfix = os.path.splitext(coorFile)[-1]
36
+ if coorPostfix == '.rst7' or coorPostfix == '.rst' or coorPostfix == '.inpcrd':
37
+ coorType = 'inpcrd'
38
+
39
+ if coorFile == '':
40
+ self.uObject = MDAnalysis.Universe(topFile)
41
+ else:
42
+ # MDAnalysis does not well recognize amber file type by postfix
43
+ coorPostfix = os.path.splitext(coorFile)[-1]
44
+ if coorPostfix == '.rst7' or coorPostfix == '.rst' or coorPostfix == '.inpcrd':
45
+ coorType = 'INPCRD'
46
+ self.uObject = MDAnalysis.Universe(topFile, coorFile, format=coorType)
47
+ else:
48
+ self.uObject = MDAnalysis.Universe(topFile, coorFile)
49
+ self.uObject.add_TopologyAttr('tempfactors')
50
+ self.topPath = topFile
51
+
52
+ def saveFile(self, selection, targetPath, targetType, saveTop=False, topPath=''):
53
+ """save the coordinate file to the target type
54
+ this function cannot really 'save' the topology file,
55
+ it can only copy the original topology to the target path
56
+
57
+ Args:
58
+ selection (str): selection of atoms to save
59
+ targetPath (str): path for the coor file to be saved
60
+ targetType (str): type of the coor file (pdb, xyz)
61
+ saveTop (bool): whether the topology file will be saved. Defaults to False.
62
+ topPath (str, optional): path for the topology file to be saved. Defaults to ''.
63
+
64
+ Raises:
65
+ SelectionError: if the selection corresponds to nothing
66
+ """
67
+
68
+ atoms = self.uObject.select_atoms(selection)
69
+
70
+ if len(atoms) == 0:
71
+ raise SelectionError('Empty selection!')
72
+
73
+ atoms.write(targetPath, targetType, bonds=None)
74
+ if saveTop:
75
+ assert(selection == 'all')
76
+ shutil.copyfile(self.topPath, topPath)
77
+
78
+ def saveNDX(self, selections, names, targetPath, nonHydrogen=True):
79
+ """save an ndx file, including the selections
80
+
81
+ Args:
82
+ selections (list of str): the selections for the ndx file
83
+ names (list of str): the name in ndx of each selection
84
+ targetPath (str): path for the file to be saved
85
+ nonHydrogen (bool, optional): whether only non-H atoms are considered. Defaults to True.
86
+
87
+ Raises:
88
+ SelectionError: if the selection corresponds to nothing
89
+ """
90
+
91
+ assert(len(selections) == len(names))
92
+
93
+ if nonHydrogen == True:
94
+ HString = 'and not (name H*)'
95
+ else:
96
+ HString = ''
97
+
98
+ for selection, name in zip(selections, names):
99
+ atoms = self.uObject.select_atoms(f'{selection} {HString}')
100
+
101
+ if len(atoms) == 0:
102
+ raise SelectionError('Empty selection!')
103
+
104
+ atoms.write(targetPath, 'ndx', name=name, mode='a')
105
+
106
+ def getResid(self, selection):
107
+ """return a string listing the resid of the selection
108
+ may be used to generate amber masks
109
+
110
+ Args:
111
+ selection (str): MDAnalysis selection
112
+
113
+ Raises:
114
+ SelectionError: if the selection corresponds to nothing
115
+
116
+ Returns:
117
+ str: a list of resid, e.g. (4,5,6,7,8,9,10)
118
+ """
119
+
120
+ atoms = self.uObject.select_atoms(selection)
121
+
122
+ if len(atoms) == 0:
123
+ raise SelectionError('Empty selection!')
124
+
125
+ return ','.join([str(num+1) for num in atoms.residues.ix])
126
+
127
+ def measureMinmax(self, selection):
128
+ """mimic VMD measure minmax
129
+
130
+ Args:
131
+ selection (str): selection of atoms
132
+
133
+ Raises:
134
+ SelectionError: if the selection corresponds to nothing
135
+
136
+ Returns:
137
+ np.array (2*3, float): ((minX, minY, minZ), (maxX, maxY, maxZ))
138
+ """
139
+
140
+ atoms = self.uObject.select_atoms(selection)
141
+
142
+ if len(atoms) == 0:
143
+ raise SelectionError('Empty selection!')
144
+
145
+ atomPositions = atoms.positions
146
+ xyz_array = np.transpose(atomPositions)
147
+ min_x = np.min(xyz_array[0])
148
+ max_x = np.max(xyz_array[0])
149
+ min_y = np.min(xyz_array[1])
150
+ max_y = np.max(xyz_array[1])
151
+ min_z = np.min(xyz_array[2])
152
+ max_z = np.max(xyz_array[2])
153
+
154
+ return np.array([[min_x, min_y, min_z],[max_x, max_y, max_z]])
155
+
156
+ def measureCenter(self, selection):
157
+ """mimic vmd measure center
158
+
159
+ Args:
160
+ selection (str): selection of atoms
161
+
162
+ Raises:
163
+ SelectionError: if the selection corresponds to nothing
164
+
165
+ Returns:
166
+ np.array (3, float): (x, y, z)
167
+ """
168
+
169
+ atoms = self.uObject.select_atoms(selection)
170
+
171
+ if len(atoms) == 0:
172
+ raise SelectionError('Empty selection!')
173
+
174
+ atomPositions = atoms.positions
175
+ xyz_array = np.transpose(atomPositions)
176
+ center_x = np.average(xyz_array[0])
177
+ center_y = np.average(xyz_array[1])
178
+ center_z = np.average(xyz_array[2])
179
+
180
+ return np.array([center_x, center_y, center_z])
181
+
182
+ def measureDistance(self, selection1, selection2):
183
+ """measure the distance between the center of mass
184
+ of two atom groups
185
+
186
+ Args:
187
+ selection1 (str): selection of atom group 1
188
+ selection2 (str): selection of atom group 2
189
+
190
+ Returns:
191
+ float: distance between the center of mass of the two atom groups
192
+ """
193
+
194
+ center1 = self.measureCenter(selection1)
195
+ center2 = self.measureCenter(selection2)
196
+ return round(np.linalg.norm(center2 - center1), 1)
197
+
198
+ def measurePBC(self):
199
+ """measure periodic boundary condition of the file
200
+
201
+ Returns:
202
+ np.array (2*3, float): ((lengthX, lengthY, lengthZ), (centerX, centerY, centerZ))
203
+ """
204
+
205
+ minmaxArray = self.measureMinmax('all')
206
+ vec = minmaxArray[1] - minmaxArray[0]
207
+ center = self.measureCenter('all')
208
+
209
+ return np.array((vec, center))
210
+
211
+ def measurePolarAngles(self, selectionPro, selectionLig):
212
+ """calculation the polar angles based on selectionPro and selectionLig
213
+
214
+ Args:
215
+ selectionPro (str): selection of the host molecule
216
+ selectionLig (str): selection of the ligand molecule
217
+
218
+ Returns:
219
+ np.array (2, float): (theta, phi) in degrees
220
+ """
221
+
222
+ vector = self.measureCenter(selectionLig) - self.measureCenter(selectionPro)
223
+ vector /= np.linalg.norm(vector)
224
+
225
+ return (float(int(math.degrees(np.arccos(-vector[1])))), float(int(math.degrees(np.arctan2(vector[2], vector[0])))))
226
+
227
+ def setBeta(self, selection, beta):
228
+ """set beta for the selected atoms
229
+
230
+ Args:
231
+ selection (str): selection of atoms to change beta
232
+ beta (int): beta value
233
+
234
+ Raises:
235
+ SelectionError: if the selection corresponds to nothing
236
+ """
237
+
238
+ atoms = self.uObject.select_atoms(selection)
239
+
240
+ if len(atoms) == 0:
241
+ raise SelectionError('Empty selection!')
242
+
243
+ atoms.tempfactors = beta
244
+
245
+ def moveSystem(self, moveVector):
246
+ """move all the atoms in the loaded file
247
+
248
+ Args:
249
+ moveVector (np.array, 3, float): the vector of moving
250
+ """
251
+
252
+ allAtoms = self.uObject.select_atoms('all')
253
+ transformations.translate(moveVector)(allAtoms)
254
+
255
+ def rotateSystem(self, axis, degrees):
256
+ """rotate all the atoms in the loaded file
257
+
258
+ Args:
259
+ axis (str): 'x' or 'y' or 'z'
260
+ degrees (float): degrees to move by
261
+ """
262
+
263
+ assert(axis == 'x' or axis == 'y' or axis == 'z')
264
+
265
+ allAtoms = self.uObject.select_atoms('all')
266
+ if axis == 'x':
267
+ axisVector = (1,0,0)
268
+ elif axis == 'y':
269
+ axisVector = (0,1,0)
270
+ else:
271
+ axisVector = (0,0,1)
272
+ transformations.rotate.rotateby(degrees, axisVector, ag=allAtoms)(allAtoms)
273
+
274
+ def centerSystem(self):
275
+ """move all the atoms in the loaded file,
276
+ such that the center of system be (0,0,0)
277
+ """
278
+
279
+ vec = self.measurePBC()[1] * -1.0
280
+ self.moveSystem(vec)
281
+
282
+ def charmmToGromacs(psfFile, pdbFile, prmFiles, PBC, outputPrefix):
283
+ """convert a set of CHARMM files (psf + pdb + prm + pbc) into the Gromacs format
284
+
285
+ Args:
286
+ psfFile (str): path of the psf file
287
+ pdbFile (str): path of the pdb file
288
+ prmFiles (list of str): pathes of the prm files
289
+ PBC (list of flost): pbc information
290
+ outputPrefix (str): path + prefix of the output file
291
+ """
292
+ struct = parmed.load_file(psfFile)
293
+ struct.load_parameters(
294
+ parmed.charmm.CharmmParameterSet(*prmFiles)
295
+ )
296
+ struct.coordinates = parmed.load_file(pdbFile).coordinates
297
+ struct.box = [PBC[0], PBC[1], PBC[2], 90, 90, 90]
298
+ struct.save(f'{outputPrefix}.top', format='gromacs')
299
+ struct.save(f'{outputPrefix}.gro')
300
+
301
+ def amberToGromacs(parmFile, rstFile, PBC, outputPrefix):
302
+ """convert a set of Amber files (parm7 + rst7) into the Gromacs format
303
+
304
+ Args:
305
+ parmFile (str): path of the parm7 file
306
+ rstFile (str): path of the rst7 file
307
+ PBC (list of flost): pbc information
308
+ outputPrefix (str): path + prefix of the output file
309
+ """
310
+ struct = parmed.load_file(parmFile, xyz=rstFile)
311
+ struct.box = [PBC[0], PBC[1], PBC[2], 90, 90, 90]
312
+ struct.save(f'{outputPrefix}.top', format='gromacs')
313
+ struct.save(f'{outputPrefix}.gro')
314
+
315
+ def gromacsToAmber(topFile, groFile, PBC, outputPrefix):
316
+ """convert a set of Gromacs files (top + gro/pdb) into the Amber format
317
+
318
+ Args:
319
+ topFile (str): path of the top file
320
+ groFile (str): path of the gro file
321
+ PBC (list of flost): pbc information
322
+ outputPrefix (str): path + prefix of the output file
323
+ """
324
+ struct = parmed.load_file(topFile, xyz=groFile)
325
+ struct.box = [PBC[0], PBC[1], PBC[2], 90, 90, 90]
326
+ struct.save(f'{outputPrefix}.parm7', format='amber')
327
+ struct.save(f'{outputPrefix}.rst7', format='rst7')
@@ -0,0 +1,218 @@
1
+ # plot figures
2
+
3
+ import math
4
+ import os
5
+ import pathlib
6
+
7
+ import matplotlib
8
+ import matplotlib.pyplot as plt
9
+ import numpy as np
10
+ from scipy import interpolate
11
+
12
+
13
+ # an runtime error
14
+ # does not have corresponding correction for a pmf
15
+ class NoCorrectionFileError(RuntimeError):
16
+ def __init__(self, arg):
17
+ self.args = arg
18
+
19
+ def isGaWTM(pmfFiles):
20
+ """determine whether the input PMFs indicate Gs-WTM simulations
21
+
22
+ Args:
23
+ pmfFiles (list[str]): path to a set of PMFs (and pmf corrections)
24
+
25
+ Returns:
26
+ bool: GaWTM simulation or not
27
+ """
28
+
29
+ for file in pmfFiles:
30
+ if ''.join(pathlib.Path(file).suffixes) == '.reweightamd1.cumulant.pmf':
31
+ return True
32
+ return False
33
+
34
+ def correctGaWTM(pmfFile):
35
+ """read a 1D namd PMF file and correct it using cumulant.pmf file
36
+
37
+ Args:
38
+ pmfFile (str): path to the pmf File
39
+
40
+ Returns:
41
+ np.array (N*2): 1D PMF
42
+ """
43
+
44
+ pmf = np.loadtxt(pmfFile)
45
+ correction_pmfFile = pmfFile.replace('.czar.pmf', '') + '.reweightamd1.cumulant.pmf'
46
+
47
+ if not os.path.exists(correction_pmfFile):
48
+ raise NoCorrectionFileError(f'{pmfFile} does not have a corresponding correction!')
49
+
50
+ correction_data = np.loadtxt(correction_pmfFile)
51
+ correction_interpolate = interpolate.interp1d(correction_data[:,0], correction_data[:,1], fill_value="extrapolate")
52
+
53
+ pmf[:,1] += correction_interpolate(pmf[:,0])
54
+
55
+ return pmf
56
+
57
+ def readPMF(pmfFile):
58
+ """read a 1D namd PMF file
59
+
60
+ Args:
61
+ pmfFile (str): path to the pmf File
62
+
63
+ Returns:
64
+ np.array (N*2): 1D PMF
65
+ """
66
+
67
+ return np.loadtxt(pmfFile)
68
+
69
+ def mergePMF(pmfFiles):
70
+ """merge several PMF files
71
+
72
+ Args:
73
+ pmfFiles (list of np.arrays): list of 1D pmfs
74
+
75
+ Returns:
76
+ np.array (N*2): merged PMF if the PMFs overlap, pmfFiles[0] otherwise
77
+ """
78
+
79
+ numPmfs = len(pmfFiles)
80
+ assert(numPmfs > 0)
81
+
82
+ # sort pmfs
83
+ pmfSort = [i for i in range(numPmfs)]
84
+ pmfSort.sort(key=lambda x: pmfFiles[x][0][0])
85
+
86
+ finalPMF = pmfFiles[pmfSort[0]]
87
+
88
+ if len(pmfFiles) > 1:
89
+ for i in range(1, len(pmfFiles)):
90
+ for j in range(len(finalPMF)):
91
+ if finalPMF[j][0] == pmfFiles[pmfSort[i]][0][0]:
92
+ # overlapped region
93
+ avgDifference = np.average(finalPMF[j:,1:] - pmfFiles[pmfSort[i]][0:len(finalPMF)-j,1:])
94
+ pmfFiles[pmfSort[i]][:,1:] += avgDifference
95
+ finalPMF[j:,1:] = (finalPMF[j:,1:] + pmfFiles[pmfSort[i]][0:len(finalPMF)-j,1:]) / 2
96
+ # other region
97
+ finalPMF = np.append(finalPMF, pmfFiles[pmfSort[i]][len(finalPMF)-j:], axis=0)
98
+ break
99
+
100
+ finalPMF[:,1] -= finalPMF[:,1].min()
101
+
102
+ return finalPMF
103
+
104
+ def writePMF(pmfFile, pmf):
105
+ """write a 1D namd PMF file
106
+
107
+ Args:
108
+ pmfFile (str): path to the pmf File
109
+ pmf (np.array, N*2): pmf to be written
110
+ """
111
+
112
+ np.savetxt(pmfFile, pmf, fmt='%g')
113
+
114
+ def plotPMF(pmf):
115
+ """plot a pmf
116
+
117
+ Args:
118
+ pmf (np.array, N*2): pmf to be plotted
119
+ """
120
+
121
+ plt.plot(pmf[:,0], pmf[:,1])
122
+ plt.xlabel('Transition coordinate')
123
+ plt.ylabel('ΔG (kcal/mol)')
124
+ plt.show()
125
+
126
+ def plotHysteresis(forwardProfile, backwardProfile):
127
+ """plot the profile describing the hysteresis between forward and backward
128
+ simulations
129
+
130
+ Args:
131
+ forwardProfile (np.array, N*2): forward free-energy profile to be plotted
132
+ backwardProfile (np.array, N*2): backward free-energy profile to be plotted
133
+ """
134
+
135
+ plt.plot(forwardProfile[:,0], forwardProfile[:,1], label='Forward')
136
+ plt.plot(backwardProfile[:,0], backwardProfile[:,1], label='Backward')
137
+ plt.xlabel('Lambda')
138
+ plt.ylabel('ΔG (kcal/mol)')
139
+ plt.legend()
140
+ plt.show()
141
+
142
+ def calcRMSD(inputArray):
143
+ """calculate RMSD of a np.array with respect to (0,0,0,...0)
144
+
145
+ Args:
146
+ inputArray (1D np.array): the input array
147
+
148
+ Returns:
149
+ float: RMSD of a np.array with respect to (0,0,0,...0)
150
+ """
151
+
152
+ sumG2 = sum(map(lambda x: x * x, inputArray))
153
+ return math.sqrt(sumG2 / len(inputArray))
154
+
155
+ def readFrame(input):
156
+ """read a frame of Colvars hist file and calculate its RMSD with respect to zero array
157
+
158
+ Args:
159
+ input (python file object): input object
160
+
161
+ Returns:
162
+ float: RMSD with respect to zero array
163
+ """
164
+
165
+ G = []
166
+ while True:
167
+ line = input.readline()
168
+
169
+ # end of file
170
+ if not line:
171
+ return False
172
+
173
+ splitedLine = line.strip().split()
174
+ if splitedLine == []:
175
+ if G == []:
176
+ continue
177
+ else:
178
+ break
179
+ if splitedLine[0].startswith('#'):
180
+ continue
181
+
182
+ G.append(float(splitedLine[1]))
183
+
184
+ if G != []:
185
+ return calcRMSD(G)
186
+ else:
187
+ return None
188
+
189
+ def parseHistFile(histPath):
190
+ """parse a hist.czar.pmf file and return frame-RMSD list
191
+
192
+ Args:
193
+ histPath (str): path to a hist.czar.pmf file
194
+
195
+ Returns:
196
+ 1D np.array: time evolution of RMSD with respect to zero array
197
+ """
198
+
199
+ rmsd = []
200
+ with open(histPath, 'r') as ifile:
201
+ while True:
202
+ rmsdPerFrame = readFrame(ifile)
203
+ if rmsdPerFrame is False:
204
+ break
205
+ rmsd.append(rmsdPerFrame)
206
+ return rmsd
207
+
208
+ def plotConvergence(rmsdList):
209
+ """plot the time evolution of PMF rmsd
210
+
211
+ Args:
212
+ rmsdList (list or 1D np.array, float): time evolution of RMSD with respect to zero array
213
+ """
214
+
215
+ plt.plot(range(1, len(rmsdList) + 1), rmsdList)
216
+ plt.xlabel('Frame')
217
+ plt.ylabel('RMSD (Colvars Unit)')
218
+ plt.show()
BFEE2/doc/Doc.pdf ADDED
Binary file
BFEE2/doc/__init__.py ADDED
@@ -0,0 +1 @@
1
+