bfee2 2.5.0__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.
Potentially problematic release.
This version of bfee2 might be problematic. Click here for more details.
- BFEE2/__init__.py +0 -0
- BFEE2/commonTools/__init__.py +0 -0
- BFEE2/commonTools/commonSlots.py +48 -0
- BFEE2/commonTools/fileParser.py +327 -0
- BFEE2/commonTools/ploter.py +218 -0
- BFEE2/doc/Doc.pdf +0 -0
- BFEE2/doc/__init__.py +1 -0
- BFEE2/gui.py +2136 -0
- BFEE2/inputGenerator.py +2857 -0
- BFEE2/postTreatment.py +502 -0
- BFEE2/templates_gromacs/000.colvars.template +37 -0
- BFEE2/templates_gromacs/000.generate_tpr_sh.template +31 -0
- BFEE2/templates_gromacs/000.mdp.template +70 -0
- BFEE2/templates_gromacs/001.colvars.template +76 -0
- BFEE2/templates_gromacs/001.generate_tpr_sh.template +31 -0
- BFEE2/templates_gromacs/001.mdp.template +70 -0
- BFEE2/templates_gromacs/001.readme.template +1 -0
- BFEE2/templates_gromacs/002.colvars.template +101 -0
- BFEE2/templates_gromacs/002.generate_tpr_sh.template +31 -0
- BFEE2/templates_gromacs/002.mdp.template +70 -0
- BFEE2/templates_gromacs/003.colvars.template +125 -0
- BFEE2/templates_gromacs/003.generate_tpr_sh.template +36 -0
- BFEE2/templates_gromacs/003.mdp.template +70 -0
- BFEE2/templates_gromacs/004.colvars.template +148 -0
- BFEE2/templates_gromacs/004.generate_tpr_sh.template +37 -0
- BFEE2/templates_gromacs/004.mdp.template +70 -0
- BFEE2/templates_gromacs/005.colvars.template +170 -0
- BFEE2/templates_gromacs/005.generate_tpr_sh.template +38 -0
- BFEE2/templates_gromacs/005.mdp.template +70 -0
- BFEE2/templates_gromacs/006.colvars.template +192 -0
- BFEE2/templates_gromacs/006.generate_tpr_sh.template +39 -0
- BFEE2/templates_gromacs/006.mdp.template +70 -0
- BFEE2/templates_gromacs/007.colvars.template +210 -0
- BFEE2/templates_gromacs/007.generate_tpr_sh.template +40 -0
- BFEE2/templates_gromacs/007.mdp.template +69 -0
- BFEE2/templates_gromacs/007_eq.colvars.template +169 -0
- BFEE2/templates_gromacs/007_eq.generate_tpr_sh.template +64 -0
- BFEE2/templates_gromacs/007_min.mdp.template +58 -0
- BFEE2/templates_gromacs/008.colvars.template +42 -0
- BFEE2/templates_gromacs/008.generate_tpr_sh.template +31 -0
- BFEE2/templates_gromacs/008.mdp.template +70 -0
- BFEE2/templates_gromacs/008_eq.generate_tpr_sh.template +31 -0
- BFEE2/templates_gromacs/BFEEGromacs.py +1244 -0
- BFEE2/templates_gromacs/__init__.py +0 -0
- BFEE2/templates_gromacs/find_min_max.awk +27 -0
- BFEE2/templates_namd/__init__.py +0 -0
- BFEE2/templates_namd/configTemplate.py +1094 -0
- BFEE2/templates_namd/fep.tcl +299 -0
- BFEE2/templates_namd/scriptTemplate.py +149 -0
- BFEE2/templates_namd/solvate.tcl +9 -0
- BFEE2/templates_namd/solvate_mem.tcl +9 -0
- BFEE2/templates_namd/updateCenters.py +311 -0
- BFEE2/templates_readme/Readme_Gromacs_Geometrical.txt +25 -0
- BFEE2/templates_readme/Readme_NAMD_Alchemical.txt +20 -0
- BFEE2/templates_readme/Readme_NAMD_Geometrical.txt +34 -0
- BFEE2/templates_readme/__init__.py +1 -0
- BFEE2/third_party/__init__.py +0 -0
- BFEE2/third_party/py_bar.py +335 -0
- BFEE2/version.py +2 -0
- bfee2-2.5.0.data/scripts/BFEE2Gui.py +18 -0
- bfee2-2.5.0.dist-info/LICENSE +677 -0
- bfee2-2.5.0.dist-info/METADATA +76 -0
- bfee2-2.5.0.dist-info/RECORD +65 -0
- bfee2-2.5.0.dist-info/WHEEL +5 -0
- bfee2-2.5.0.dist-info/top_level.txt +1 -0
BFEE2/postTreatment.py
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
# post-treatment of BFEE
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from BFEE2.third_party import py_bar
|
|
8
|
+
|
|
9
|
+
# Boltazann constant for NAMD unit convention
|
|
10
|
+
BOLTZMANN = 0.0019872041
|
|
11
|
+
BOLTZMANN_GMX = 0.0019872041 * 4.184
|
|
12
|
+
|
|
13
|
+
# standard concentration
|
|
14
|
+
CSTAR = 1661
|
|
15
|
+
CSTAR_GMX = 1.661
|
|
16
|
+
|
|
17
|
+
# an runtime error
|
|
18
|
+
# r* > r(pmf)
|
|
19
|
+
class RStarTooLargeError(RuntimeError):
|
|
20
|
+
def __init__(self, arg):
|
|
21
|
+
self.args = arg
|
|
22
|
+
|
|
23
|
+
class postTreatment:
|
|
24
|
+
"""the post-treatment of BFEE outputs
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, temperature, unit, jobType='geometric'):
|
|
28
|
+
"""do post treatment, internally, all the unit should be converted into
|
|
29
|
+
the NAMD/Colvars unit
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
temperature (float): temperature of the simulation
|
|
33
|
+
unit (str): unit convention used by MD engine, 'namd' or 'gromacs'
|
|
34
|
+
jobType (str): 'geometric' or 'alchemical'. Actually this arg is not used yet. Default to geometric.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
if unit == 'namd':
|
|
38
|
+
self.BOLTZMANN = BOLTZMANN
|
|
39
|
+
self.CSTAR = CSTAR
|
|
40
|
+
elif unit == 'gromacs':
|
|
41
|
+
self.BOLTZMANN = BOLTZMANN_GMX
|
|
42
|
+
self.CSTAR = CSTAR_GMX
|
|
43
|
+
|
|
44
|
+
self.unit = unit
|
|
45
|
+
self.beta = 1 / (self.BOLTZMANN * temperature)
|
|
46
|
+
self.temperature = float(temperature)
|
|
47
|
+
|
|
48
|
+
def _readPMF(self, filePath):
|
|
49
|
+
"""read a 1D PMF file
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
filePath (str): the path of the PMF file
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
np.array (float, 2*N): ((x0,x1,x2, ...), (y0, y1, y2, ...))
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
data = np.loadtxt(filePath)
|
|
59
|
+
x = data[:,0]
|
|
60
|
+
y = data[:,1]
|
|
61
|
+
|
|
62
|
+
return np.array((x, y))
|
|
63
|
+
|
|
64
|
+
def _geometricRestraintContribution(self, pmf, forceConstant, rmsd=False, unbound=False):
|
|
65
|
+
"""calculate the contribution of RMSD and angle restraints
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
pmf (np.array, float, 2*N): ((x0,x1,x2, ...), (y0, y1, y2, ...))
|
|
69
|
+
forceConstant (float): the force constant of the restraint
|
|
70
|
+
rmsd (bool): whether the contribution of RMSD is being calculated. Defaults to False.
|
|
71
|
+
unbound (bool, optional): whether unbound-state contribution is being calculated. Defaults to False.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
float: contribution of the geometric restraint
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
width = pmf[0][1] - pmf[0][0]
|
|
78
|
+
|
|
79
|
+
if rmsd:
|
|
80
|
+
# for RMSD, the restraintCenter is zero
|
|
81
|
+
restraintCenter = 0
|
|
82
|
+
else:
|
|
83
|
+
# the minimum of pmf
|
|
84
|
+
restraintCenter = pmf[0][np.argmin(pmf[1])]
|
|
85
|
+
|
|
86
|
+
# integration
|
|
87
|
+
numerator = 0
|
|
88
|
+
denominator = 0
|
|
89
|
+
for x, y in zip(pmf[0], pmf[1]):
|
|
90
|
+
numerator += math.exp(-self.beta * y)
|
|
91
|
+
denominator += math.exp((-self.beta) * (y + 0.5 * forceConstant * ((x - restraintCenter)**2)))
|
|
92
|
+
|
|
93
|
+
contribution = math.log(numerator / denominator) / self.beta
|
|
94
|
+
|
|
95
|
+
if unbound:
|
|
96
|
+
return contribution
|
|
97
|
+
else:
|
|
98
|
+
return -contribution
|
|
99
|
+
|
|
100
|
+
def _geometricRestraintContributionBulk(
|
|
101
|
+
self, theta, forceConstantTheta, forceConstantPhi, forceConstantPsi
|
|
102
|
+
):
|
|
103
|
+
"""contribution of rotational restraints in the unbounded state
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
theta (float): restraining center of the theta angle
|
|
107
|
+
forceConstantTheta (float): restraining force constant for Theta
|
|
108
|
+
forceConstantPhi (float): restraining force constant for Phi
|
|
109
|
+
forceConstantPsi (float): restraining force constant for Psi
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
float: contribution of the geometric restraint in the unbound state
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
# all the units in radian
|
|
116
|
+
theta0 = math.radians(theta)
|
|
117
|
+
# periodic CV then the u(phi) and u(psi) should be the same in all cases
|
|
118
|
+
phi0 = math.radians(180)
|
|
119
|
+
psi0 = math.radians(180)
|
|
120
|
+
|
|
121
|
+
forceConstantTheta *= (180 / math.pi)**2
|
|
122
|
+
forceConstantPhi *= (180 / math.pi)**2
|
|
123
|
+
forceConstantPsi *= (180 / math.pi)**2
|
|
124
|
+
|
|
125
|
+
contributionTheta = 0
|
|
126
|
+
contributionPhi = 0
|
|
127
|
+
contributionPsi = 0
|
|
128
|
+
# integration
|
|
129
|
+
for i in range(1000):
|
|
130
|
+
theta = i / 1000.0 * math.pi - math.pi / 2
|
|
131
|
+
contributionTheta += 1.0/1000.0 * math.pi * math.sin(theta + math.pi / 2) * \
|
|
132
|
+
math.exp(-self.beta * 0.5 * forceConstantTheta * ((theta - theta0)**2))
|
|
133
|
+
|
|
134
|
+
phi = i / 1000.0 * 2 * math.pi
|
|
135
|
+
contributionPhi += 1.0/1000.0 * 2 * math.pi * \
|
|
136
|
+
math.exp(-self.beta * 0.5 * forceConstantPhi* ((phi - phi0)**2))
|
|
137
|
+
|
|
138
|
+
psi = i / 1000.0 * 2 * math.pi
|
|
139
|
+
contributionPsi += 1.0/1000.0 * 2 * math.pi * \
|
|
140
|
+
math.exp(-self.beta * 0.5 * forceConstantPsi* ((psi - psi0)**2))
|
|
141
|
+
|
|
142
|
+
return -math.log((contributionTheta * contributionPhi * contributionPsi) / 8 / math.pi**2) / self.beta
|
|
143
|
+
|
|
144
|
+
def _geometricJacobianCorrection(self, pmf):
|
|
145
|
+
"""correct the Jacobian contribution of separation pmf
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
pmf (np.array, float, 2*N): ((x0,x1,x2, ...), (y0, y1, y2, ...)), separation pmf
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
for i in range(len(pmf[0])):
|
|
152
|
+
pmf[1][i] += 2 * self.BOLTZMANN * self.temperature * math.log(pmf[0][i])
|
|
153
|
+
|
|
154
|
+
pmf[1] -= np.min(pmf[1])
|
|
155
|
+
|
|
156
|
+
def _geometricCalculateSI(
|
|
157
|
+
self, rStar, pmf, polarTheta, polarPhi, forceConstantPolarTheta, forceConstantPolarPhi):
|
|
158
|
+
"""calculation the contribution of S* and I* in the separation simulation
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
rStar (float): r* in integration
|
|
162
|
+
pmf (np.array, float, 2*N): ((x0,x1,x2, ...), (y0, y1, y2, ...)), separation pmf
|
|
163
|
+
polarTheta0 (float): restraining center of polarTheta
|
|
164
|
+
polarPhi0 (float): restraining center of polarPhi
|
|
165
|
+
forceConstantPolarTheta (float): restraining force constant for polarTheta
|
|
166
|
+
forceConstantPolarPhi (float): restraining force constant for polarPhi
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
float: contribution of S* and I* in the separation simulation
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
if rStar > pmf[0][-1]:
|
|
173
|
+
raise RStarTooLargeError('r_star cannot be larger than r_max of step 7!')
|
|
174
|
+
|
|
175
|
+
polarTheta0 = math.radians(polarTheta)
|
|
176
|
+
polarPhi0 = math.radians(polarPhi)
|
|
177
|
+
|
|
178
|
+
forceConstantPolarTheta *= (180 / math.pi)**2
|
|
179
|
+
forceConstantPolarPhi *= (180 / math.pi)**2
|
|
180
|
+
|
|
181
|
+
contributionPolarTheta = 0
|
|
182
|
+
contributionPolarPhi = 0
|
|
183
|
+
# integration
|
|
184
|
+
for i in range(1000):
|
|
185
|
+
polarTheta = i / 1000.0 * math.pi
|
|
186
|
+
contributionPolarTheta += 1.0 / 1000.0 * math.pi * math.sin(polarTheta) * \
|
|
187
|
+
math.exp(-self.beta * 0.5 * forceConstantPolarTheta * (polarTheta - polarTheta0)**2)
|
|
188
|
+
|
|
189
|
+
polarPhi = i / 1000.0 * 2 * math.pi - math.pi
|
|
190
|
+
contributionPolarPhi += 1.0 / 1000.0 * 2 * math.pi * \
|
|
191
|
+
math.exp(-self.beta * 0.5 * forceConstantPolarPhi * (polarPhi - polarPhi0)**2)
|
|
192
|
+
|
|
193
|
+
S = rStar**2 * contributionPolarTheta * contributionPolarPhi
|
|
194
|
+
|
|
195
|
+
# w(r*)
|
|
196
|
+
wrStar = pmf[1][0]
|
|
197
|
+
for x, y in zip(pmf[0], pmf[1]):
|
|
198
|
+
if x >= rStar:
|
|
199
|
+
wrStar = y
|
|
200
|
+
break
|
|
201
|
+
|
|
202
|
+
# integration
|
|
203
|
+
width = pmf[0][1] - pmf[0][0]
|
|
204
|
+
I = 0
|
|
205
|
+
for x, y in zip(pmf[0], pmf[1]):
|
|
206
|
+
I += width * math.exp(-self.beta * (y - wrStar))
|
|
207
|
+
if x >= rStar:
|
|
208
|
+
break
|
|
209
|
+
|
|
210
|
+
return -1 / self.beta * math.log(S * I / self.CSTAR)
|
|
211
|
+
|
|
212
|
+
def geometricBindingFreeEnergy(self, filePathes, parameters):
|
|
213
|
+
"""calculate binding free energy for geometric route
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
filePathes (list of strings, 8): pathes of PMF files for step1 - step8.
|
|
217
|
+
PMFs for steps 1 and 8 can be omitted, which
|
|
218
|
+
indicates the investication of a rigid ligand.
|
|
219
|
+
parameters (np.array, floats, 8): (forceConstant1, FC2, FC3, FC4, FC5, FC6, r*, FC8)
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
np.array, float, 10: (contributions for step1, 2, 3, 4 ... 8, bulk restraining contribution, free energy)
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
assert len(parameters) == 8
|
|
226
|
+
assert len(filePathes) == 8
|
|
227
|
+
|
|
228
|
+
pmfs = []
|
|
229
|
+
rigid_ligand = False
|
|
230
|
+
for index, path in enumerate(filePathes):
|
|
231
|
+
if (index == 0 or index == 7) and path == '':
|
|
232
|
+
rigid_ligand = True
|
|
233
|
+
pmfs.append(None)
|
|
234
|
+
else:
|
|
235
|
+
pmfs.append(self._readPMF(path))
|
|
236
|
+
self._geometricJacobianCorrection(pmfs[6])
|
|
237
|
+
|
|
238
|
+
contributions = np.zeros(10)
|
|
239
|
+
if not rigid_ligand:
|
|
240
|
+
contributions[0] = self._geometricRestraintContribution(pmfs[0], parameters[0], True, False)
|
|
241
|
+
else:
|
|
242
|
+
contributions[0] = 0.0
|
|
243
|
+
contributions[1] = self._geometricRestraintContribution(pmfs[1], parameters[1], False, False)
|
|
244
|
+
contributions[2] = self._geometricRestraintContribution(pmfs[2], parameters[2], False, False)
|
|
245
|
+
contributions[3] = self._geometricRestraintContribution(pmfs[3], parameters[3], False, False)
|
|
246
|
+
contributions[4] = self._geometricRestraintContribution(pmfs[4], parameters[4], False, False)
|
|
247
|
+
contributions[5] = self._geometricRestraintContribution(pmfs[5], parameters[5], False, False)
|
|
248
|
+
contributions[6] = self._geometricCalculateSI(
|
|
249
|
+
parameters[6], pmfs[6], pmfs[4][0][np.argmin(pmfs[4][1])], pmfs[5][0][np.argmin(pmfs[5][1])],
|
|
250
|
+
parameters[4], parameters[5]
|
|
251
|
+
)
|
|
252
|
+
if not rigid_ligand:
|
|
253
|
+
contributions[7] = self._geometricRestraintContribution(pmfs[7], parameters[7], True, True)
|
|
254
|
+
else:
|
|
255
|
+
contributions[7] = 0.0
|
|
256
|
+
contributions[8] = self._geometricRestraintContributionBulk(
|
|
257
|
+
pmfs[1][0][np.argmin(pmfs[1][1])], parameters[1], parameters[2], parameters[3]
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
contributions[9] = np.sum(contributions[:9])
|
|
261
|
+
|
|
262
|
+
if self.unit == 'namd':
|
|
263
|
+
return contributions
|
|
264
|
+
elif self.unit == 'gromacs':
|
|
265
|
+
return contributions / 4.184
|
|
266
|
+
|
|
267
|
+
def _alchemicalRestraintContributionBulk(
|
|
268
|
+
self, eulerTheta, polarTheta, R,
|
|
269
|
+
forceConstantTheta=0.1, forceConstantPhi=0.1, forceConstantPsi=0.1,
|
|
270
|
+
forceConstanttheta=0.1, forceConstantphi=0.1, forceConstantR=10
|
|
271
|
+
):
|
|
272
|
+
"""contribution of (standard concentration corrected) rotational
|
|
273
|
+
and orienetational restraints in the unbounded state
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
eulerTheta (float): restraining center of the Euler angle theta
|
|
277
|
+
polarTheta (float): restraining center of the polar angle theta
|
|
278
|
+
R (float): restraining center of anger R
|
|
279
|
+
forceConstantTheta (float): restraining force constant for euler Theta. Defaults to 0.1.
|
|
280
|
+
forceConstantPhi (float, optional): restraining force constant for euler Phi. Defaults to 0.1.
|
|
281
|
+
forceConstantPsi (float, optional): restraining force constant for euler Psi. Defaults to 0.1.
|
|
282
|
+
forceConstanttheta (float, optional): restraining force constant for polar theta. Defaults to 0.1.
|
|
283
|
+
forceConstantphi (float, optional): restraining force constant for polar phi. Defaults to 0.1.
|
|
284
|
+
forceConstantR (int, optional): restraining force constant for distance R. Defaults to 10.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
float: contribution of the geometric restraint in the unbound state
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
# degrees to rad
|
|
291
|
+
eulerTheta = math.radians(eulerTheta + 90)
|
|
292
|
+
polarTheta = math.radians(polarTheta)
|
|
293
|
+
forceConstantTheta *= (180 / math.pi)**2
|
|
294
|
+
forceConstantPhi *= (180 / math.pi)**2
|
|
295
|
+
forceConstantPsi *= (180 / math.pi)**2
|
|
296
|
+
forceConstanttheta *= (180 / math.pi)**2
|
|
297
|
+
forceConstantphi *= (180 / math.pi)**2
|
|
298
|
+
|
|
299
|
+
contribution = self.BOLTZMANN * self.temperature * math.log(
|
|
300
|
+
8 * (math.pi**2) * self.CSTAR / ((R**2) * math.sin(eulerTheta) * math.sin(polarTheta)) * \
|
|
301
|
+
math.sqrt(forceConstantTheta * forceConstantPhi * forceConstantPsi * forceConstanttheta * \
|
|
302
|
+
forceConstantphi * forceConstantR ) / ((2 * math.pi * self.BOLTZMANN * self.temperature)**3)
|
|
303
|
+
)
|
|
304
|
+
return contribution
|
|
305
|
+
|
|
306
|
+
def _fepoutFile(self, filePath):
|
|
307
|
+
"""parse a fepout file and return the lambda-free energy relationship
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
filePath (str): path of the fepout file
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
tuple (2D np.array): lambda-free energy relationship
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
Lambda = []
|
|
317
|
+
dA_dLambda = []
|
|
318
|
+
|
|
319
|
+
with open(filePath, 'r', encoding='utf-8') as fepoutFile:
|
|
320
|
+
for line in fepoutFile.readlines():
|
|
321
|
+
if not line.startswith('#Free energy'):
|
|
322
|
+
continue
|
|
323
|
+
splitedLine = line.strip().split()
|
|
324
|
+
Lambda.append((float(splitedLine[7]) + float(splitedLine[8])) / 2)
|
|
325
|
+
dA_dLambda.append(float(splitedLine[11]))
|
|
326
|
+
|
|
327
|
+
if Lambda[0] > Lambda[1]:
|
|
328
|
+
Lambda.reverse()
|
|
329
|
+
dA_dLambda.reverse()
|
|
330
|
+
|
|
331
|
+
return np.array((Lambda, np.cumsum(dA_dLambda)))
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _tiLogFile(self, filePath, rigidLigand = False):
|
|
335
|
+
"""parse a ti log file and return the lambda-free energy relationship
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
filePath (str): path of the fepout file
|
|
339
|
+
rigidLigand (bool): whether dealing with a rigid ligand. Default to False.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
tuple (2D np.array): lambda-free energy relationship
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
Lambda = []
|
|
346
|
+
dA_dLambda = []
|
|
347
|
+
|
|
348
|
+
if rigidLigand:
|
|
349
|
+
numCVs = 6
|
|
350
|
+
else:
|
|
351
|
+
numCVs = 7
|
|
352
|
+
|
|
353
|
+
with open(filePath, 'r', encoding='utf-8') as fepoutFile:
|
|
354
|
+
for line in fepoutFile.readlines():
|
|
355
|
+
if not ('dA/dLambda' in line):
|
|
356
|
+
continue
|
|
357
|
+
splitedLine = line.strip().split()
|
|
358
|
+
Lambda.append(float(splitedLine[4]))
|
|
359
|
+
dA_dLambda.append(float(splitedLine[6]))
|
|
360
|
+
|
|
361
|
+
# seven CVs in total with the same Lambda in the step 2
|
|
362
|
+
if Lambda[0] == Lambda[1]:
|
|
363
|
+
correctedLambda = []
|
|
364
|
+
correctedDA_dLambda = []
|
|
365
|
+
|
|
366
|
+
for i in range(0, len(Lambda), numCVs):
|
|
367
|
+
correctedLambda.append(Lambda[i])
|
|
368
|
+
totalDA_dLambda = 0
|
|
369
|
+
for j in range(numCVs):
|
|
370
|
+
totalDA_dLambda += dA_dLambda[i+j]
|
|
371
|
+
correctedDA_dLambda.append(totalDA_dLambda)
|
|
372
|
+
|
|
373
|
+
Lambda = correctedLambda
|
|
374
|
+
dA_dLambda = correctedDA_dLambda
|
|
375
|
+
|
|
376
|
+
if Lambda[0] > Lambda[1]:
|
|
377
|
+
Lambda.reverse()
|
|
378
|
+
dA_dLambda.reverse()
|
|
379
|
+
|
|
380
|
+
for i in range(1, len(Lambda)):
|
|
381
|
+
dA_dLambda[i] = (Lambda[i] - Lambda[i-1]) * dA_dLambda[i]
|
|
382
|
+
|
|
383
|
+
return np.array((Lambda, np.cumsum(dA_dLambda)))
|
|
384
|
+
|
|
385
|
+
def _alchemicalFepoutFile(self, filePath, fileType = 'fepout', rigidLigand = False):
|
|
386
|
+
"""parse a fepout/log file and return the total free energy change
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
filePath (str): path of the fepout file
|
|
390
|
+
fileType (str): 'fepout' (decouping atoms) or 'log' (decoupling restraints). Defaults to 'fepout'.
|
|
391
|
+
rigidLigand (bool): whether dealing with a rigid ligand. Default to False.
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
float: free-energy change
|
|
395
|
+
"""
|
|
396
|
+
|
|
397
|
+
if fileType == 'fepout':
|
|
398
|
+
_, freeEnergyProfile = self._fepoutFile(filePath)
|
|
399
|
+
|
|
400
|
+
if fileType == 'log':
|
|
401
|
+
_, freeEnergyProfile = self._tiLogFile(filePath, rigidLigand)
|
|
402
|
+
|
|
403
|
+
return freeEnergyProfile[-1]
|
|
404
|
+
|
|
405
|
+
def alchemicalFreeEnergy(self, forwardFilePath, backwardFilePath = '', temperature = 300, jobType = 'fep'):
|
|
406
|
+
""" parse a pair of fepout file, or a single double-wide file using the py_bar library
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
forwardFilePath (str): path to the forward fepout file
|
|
410
|
+
backwardFilePath (str): path to the backward fepout file. Empty string
|
|
411
|
+
corresponds to a double-wide simulation
|
|
412
|
+
temperature (float): temperature of the simulation
|
|
413
|
+
jobType (str, optional): Type of the post-treatment method. 'fep' or 'bar'.
|
|
414
|
+
Defaults to 'fep'.
|
|
415
|
+
Returns:
|
|
416
|
+
tuple[float, float]: free-energy change, error
|
|
417
|
+
"""
|
|
418
|
+
window, deltaU = py_bar.NAMDParser(forwardFilePath, backwardFilePath).get_data()
|
|
419
|
+
analyzer = py_bar.FEPAnalyzer(window, deltaU, temperature)
|
|
420
|
+
|
|
421
|
+
if jobType == 'bar':
|
|
422
|
+
result = analyzer.BAR_free_energy(block_size=50, n_bootstrap=20)
|
|
423
|
+
else:
|
|
424
|
+
result = analyzer.FEP_free_energy()
|
|
425
|
+
|
|
426
|
+
freeEnergy = np.sum(result[1])
|
|
427
|
+
error = np.sqrt(np.sum(np.power(result[2], 2)))
|
|
428
|
+
|
|
429
|
+
return freeEnergy, error
|
|
430
|
+
|
|
431
|
+
def alchemicalBindingFreeEnergy(self, filePathes, parameters, temperature = 300, jobType = 'fep', rigidLigand = False):
|
|
432
|
+
"""calculate binding free energy for alchemical route
|
|
433
|
+
|
|
434
|
+
Args:
|
|
435
|
+
filePathes (list of strings, 8): pathes of alchemical output files
|
|
436
|
+
(step1-forward, step1-backward, step2-forward ...)
|
|
437
|
+
parameters (np.array, floats, 9): (eulerTheta, polarTheta, r, forceConstant1, FC2, FC3, FC4, FC5, FC6)
|
|
438
|
+
temperature (float): temperature of the simulation
|
|
439
|
+
jobType (str, optional): Type of the post-treatment method. 'fep' or 'bar'.
|
|
440
|
+
Defaults to 'fep'.
|
|
441
|
+
rigidLigand (bool): whether dealing with a rigid ligand. Default to False.
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
tuple:
|
|
445
|
+
np.array, float, 6: (contributions for step1, 2, 3, 4, bulk restraining contribution, free energy)
|
|
446
|
+
np.array, float, 6: errors corresponding each contribution
|
|
447
|
+
"""
|
|
448
|
+
|
|
449
|
+
assert len(parameters) == 9
|
|
450
|
+
assert len(filePathes) == 8
|
|
451
|
+
|
|
452
|
+
rigid_ligand = False
|
|
453
|
+
if (filePathes[6] == ''):
|
|
454
|
+
rigid_ligand = True
|
|
455
|
+
|
|
456
|
+
# get free energies from fep outputs
|
|
457
|
+
freeEnergies = []
|
|
458
|
+
for i in range(len(filePathes)):
|
|
459
|
+
if filePathes[i] != '':
|
|
460
|
+
if (i // 2) % 2 == 0:
|
|
461
|
+
# just a dirty solution
|
|
462
|
+
freeEnergies.append(None)
|
|
463
|
+
#freeEnergies.append(self._alchemicalFepoutFile(filePathes[i], 'fepout'))
|
|
464
|
+
else:
|
|
465
|
+
freeEnergies.append(self._alchemicalFepoutFile(filePathes[i], 'log', rigidLigand))
|
|
466
|
+
else:
|
|
467
|
+
# backward file can be empty
|
|
468
|
+
freeEnergies.append(None)
|
|
469
|
+
|
|
470
|
+
contributions = np.zeros(6)
|
|
471
|
+
errors = np.zeros(6)
|
|
472
|
+
|
|
473
|
+
contributions[0], errors[0] = self.alchemicalFreeEnergy(filePathes[0], filePathes[1], temperature, jobType)
|
|
474
|
+
|
|
475
|
+
if freeEnergies[3] is not None:
|
|
476
|
+
contributions[1] = -(freeEnergies[2] + freeEnergies[3]) / 2
|
|
477
|
+
errors[1] = abs((freeEnergies[2] - freeEnergies[3]) / 1.414)
|
|
478
|
+
else:
|
|
479
|
+
contributions[1] = -freeEnergies[2]
|
|
480
|
+
errors[1] = 99999
|
|
481
|
+
|
|
482
|
+
contributions[2], errors[2] = self.alchemicalFreeEnergy(filePathes[4], filePathes[5], temperature, jobType)
|
|
483
|
+
contributions[2] = -contributions[2]
|
|
484
|
+
|
|
485
|
+
if not rigid_ligand:
|
|
486
|
+
if freeEnergies[7] is not None:
|
|
487
|
+
contributions[3] = (freeEnergies[6] + freeEnergies[7]) / 2
|
|
488
|
+
errors[3] = abs((freeEnergies[6] - freeEnergies[7]) / 1.414)
|
|
489
|
+
else:
|
|
490
|
+
contributions[3] = freeEnergies[6]
|
|
491
|
+
errors[3] = 99999
|
|
492
|
+
else:
|
|
493
|
+
contributions[3] = 0
|
|
494
|
+
errors[3] = 0
|
|
495
|
+
|
|
496
|
+
contributions[4] = self._alchemicalRestraintContributionBulk(*parameters)
|
|
497
|
+
errors[4] = 0
|
|
498
|
+
|
|
499
|
+
contributions[5] = contributions[0] + contributions[1] + contributions[2] + contributions[3] + contributions[4]
|
|
500
|
+
errors[5] = math.sqrt(errors[0]**2 + errors[1]**2 +errors[2]**2 + errors[3]**2 + errors[4]**2)
|
|
501
|
+
|
|
502
|
+
return contributions, errors
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
colvarsTrajFrequency 500
|
|
2
|
+
colvarsRestartFrequency 50000
|
|
3
|
+
indexFile colvars.ndx
|
|
4
|
+
|
|
5
|
+
colvar {
|
|
6
|
+
name translation
|
|
7
|
+
distance {
|
|
8
|
+
group1 {
|
|
9
|
+
indexGroup $protein_selection
|
|
10
|
+
}
|
|
11
|
+
group2 {
|
|
12
|
+
dummyAtom $protein_center
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
harmonic {
|
|
18
|
+
colvars translation
|
|
19
|
+
centers 0.0
|
|
20
|
+
forceConstant 4184
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
colvar {
|
|
24
|
+
name orientation
|
|
25
|
+
orientation {
|
|
26
|
+
atoms {
|
|
27
|
+
indexGroup $protein_selection
|
|
28
|
+
}
|
|
29
|
+
refPositionsfile reference.xyz
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
harmonic {
|
|
34
|
+
colvars orientation
|
|
35
|
+
centers (1.0, 0.0, 0.0, 0.0)
|
|
36
|
+
forceConstant 836.8
|
|
37
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
# define the binary of gromacs
|
|
4
|
+
if which gmx_mpi &> /dev/null; then
|
|
5
|
+
GMX_BIN=$(which gmx_mpi)
|
|
6
|
+
else which gmx &> /dev/null;
|
|
7
|
+
GMX_BIN=$(which gmx)
|
|
8
|
+
fi
|
|
9
|
+
echo "Will use the gromacs executable binary at $GMX_BIN"
|
|
10
|
+
|
|
11
|
+
# input (.gro, .top and .mdp) and output (.tpr)
|
|
12
|
+
# MDP_FILE: running parameters
|
|
13
|
+
# GRO_FILE: atom positions
|
|
14
|
+
# TOP_FILE: topology
|
|
15
|
+
MDP_FILE=$MDP_FILE_TEMPLATE
|
|
16
|
+
GRO_FILE=$GRO_FILE_TEMPLATE
|
|
17
|
+
TOP_FILE=$TOP_FILE_TEMPLATE
|
|
18
|
+
|
|
19
|
+
# generate the tpr file
|
|
20
|
+
OUTPUT_BASENAME=$(basename "$(pwd)")
|
|
21
|
+
TPR_FILE="$OUTPUT_BASENAME.tpr"
|
|
22
|
+
NEW_MDP_FILE="$OUTPUT_BASENAME.mdp"
|
|
23
|
+
echo "Making gromacs tpr file ($TPR_FILE)..."
|
|
24
|
+
$GMX_BIN grompp -f $MDP_FILE -c $GRO_FILE -p $TOP_FILE -o $TPR_FILE -po $NEW_MDP_FILE
|
|
25
|
+
|
|
26
|
+
# run the PMF calculation
|
|
27
|
+
mkdir -p output
|
|
28
|
+
DEFAULT_OUTPUT_FILENAME="output/$OUTPUT_BASENAME.out"
|
|
29
|
+
# $GMX_BIN mdrun -ntmpi 1 -ntomp 4 -nb gpu -pme gpu -gpu_id 0 -s $TPR_FILE -deffnm $DEFAULT_OUTPUT_FILENAME -colvars colvar.dat
|
|
30
|
+
echo "You can now run gromacs by the following example:"
|
|
31
|
+
echo "$GMX_BIN mdrun -ntmpi 1 -ntomp 4 -nb gpu -pme gpu -gpu_id 0 -s $TPR_FILE -deffnm $DEFAULT_OUTPUT_FILENAME -colvars $COLVARS_INPUT_TEMPLATE"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
;equilibration of p41-abl
|
|
2
|
+
|
|
3
|
+
;Run control
|
|
4
|
+
integrator = md
|
|
5
|
+
dt = $dt
|
|
6
|
+
tinit = 0
|
|
7
|
+
nsteps = $nsteps
|
|
8
|
+
comm-mode = Linear
|
|
9
|
+
nstcomm = 1000
|
|
10
|
+
|
|
11
|
+
;Langevin dynamics
|
|
12
|
+
bd-fric = 0
|
|
13
|
+
ld-seed = -1
|
|
14
|
+
|
|
15
|
+
;Energy minimization
|
|
16
|
+
;emtol = 10
|
|
17
|
+
;emstep = 0.01
|
|
18
|
+
|
|
19
|
+
;Output control
|
|
20
|
+
nstxout = 5000
|
|
21
|
+
nstvout = 5000
|
|
22
|
+
nstfout = 5000
|
|
23
|
+
nstlog = 5000
|
|
24
|
+
nstcalcenergy = 1000
|
|
25
|
+
nstenergy = 5000
|
|
26
|
+
|
|
27
|
+
;Neighbor searching
|
|
28
|
+
cutoff-scheme = Verlet
|
|
29
|
+
ns-type = grid
|
|
30
|
+
pbc = xyz
|
|
31
|
+
periodic-molecules = no
|
|
32
|
+
|
|
33
|
+
;Electrostatics
|
|
34
|
+
coulombtype = PME
|
|
35
|
+
coulomb-modifier = Potential-shift-Verlet
|
|
36
|
+
rcoulomb = 1.2
|
|
37
|
+
|
|
38
|
+
;VdW
|
|
39
|
+
vdwtype = Cut-off
|
|
40
|
+
vdw-modifier = force-switch
|
|
41
|
+
rvdw = 1.2
|
|
42
|
+
rvdw-switch = 1.0
|
|
43
|
+
DispCorr = no
|
|
44
|
+
|
|
45
|
+
;Bonds (for minimization)
|
|
46
|
+
constraint-algorithm = LINCS
|
|
47
|
+
constraints = h-bonds
|
|
48
|
+
continuation = yes
|
|
49
|
+
morse = no
|
|
50
|
+
|
|
51
|
+
;Implicit solvent
|
|
52
|
+
implicit-solvent = no
|
|
53
|
+
|
|
54
|
+
;Pressure coupling
|
|
55
|
+
;Pressure coupling
|
|
56
|
+
pcoupl = C-rescale
|
|
57
|
+
pcoupltype = Isotropic
|
|
58
|
+
nstpcouple = -1
|
|
59
|
+
tau-p = 2
|
|
60
|
+
compressibility = 4.5e-5
|
|
61
|
+
ref-p = $pressure
|
|
62
|
+
|
|
63
|
+
;Temperature coupling
|
|
64
|
+
tcoupl = v-rescale
|
|
65
|
+
tc-grps = System
|
|
66
|
+
tau_t = 0.1
|
|
67
|
+
ref_t = $temperature
|
|
68
|
+
|
|
69
|
+
;Velocity generation
|
|
70
|
+
gen-vel = no
|