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.
- 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 +2785 -0
- BFEE2/inputGenerator.py +2949 -0
- BFEE2/postTreatment.py +676 -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 +74 -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 +73 -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 +73 -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 +73 -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 +74 -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 +74 -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 +74 -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 +73 -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 +62 -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 +74 -0
- BFEE2/templates_gromacs/008_eq.colvars.template +14 -0
- BFEE2/templates_gromacs/008_eq.generate_tpr_sh.template +31 -0
- BFEE2/templates_gromacs/BFEEGromacs.py +1268 -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 +1152 -0
- BFEE2/templates_namd/fep.tcl +299 -0
- BFEE2/templates_namd/fep_lddm.tcl +312 -0
- BFEE2/templates_namd/scriptTemplate.py +304 -0
- BFEE2/templates_namd/solvate.tcl +9 -0
- BFEE2/templates_namd/solvate_mem.tcl +9 -0
- BFEE2/templates_namd/updateCenters.py +312 -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/templates_readme/rags.py +187 -0
- BFEE2/third_party/__init__.py +0 -0
- BFEE2/third_party/py_bar.py +585 -0
- BFEE2/version.py +4 -0
- bfee2-3.1.1.post1.data/scripts/BFEE2Gui.py +19 -0
- bfee2-3.1.1.post1.dist-info/METADATA +86 -0
- bfee2-3.1.1.post1.dist-info/RECORD +68 -0
- bfee2-3.1.1.post1.dist-info/WHEEL +5 -0
- bfee2-3.1.1.post1.dist-info/licenses/LICENSE +677 -0
- bfee2-3.1.1.post1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
import sys, math
|
|
2
|
+
from typing import List, Literal, Optional, Tuple
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
from numpy.typing import NDArray
|
|
6
|
+
|
|
7
|
+
# VERSION
|
|
8
|
+
VERSION = 1.0
|
|
9
|
+
|
|
10
|
+
# boltzmann constant
|
|
11
|
+
BOLTZMANN = 0.0019872041
|
|
12
|
+
CSTAR = 1661
|
|
13
|
+
|
|
14
|
+
# type of FEP files
|
|
15
|
+
fep_type = Literal['forward', 'backward', 'double-wide']
|
|
16
|
+
|
|
17
|
+
class NAMDParser:
|
|
18
|
+
""" parse NAMD .fepout files and get the necessary data
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, forward_file: str, backward_file: Optional[str] = '') -> None:
|
|
22
|
+
""" Read NAMD .fepout files. The end-user can either provide the bidirectional
|
|
23
|
+
.fepout files, or provide an .fepout file of the double-wide FEP simulation
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
forward_file (str): the fepout file for forward or double-wide simulation
|
|
27
|
+
backward_file (Optional[str], optional): the fepout file for backward simulation.
|
|
28
|
+
Defaults to ''.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
self._double_wide = False
|
|
32
|
+
if (backward_file == '') or (backward_file is None):
|
|
33
|
+
self._double_wide = True
|
|
34
|
+
|
|
35
|
+
# List[Tuple[float, float]], recording the boundaries of each window
|
|
36
|
+
# List[Tuple[NDArray, NDArray]], recording the deltaU of forward of
|
|
37
|
+
# backward simulations of each window
|
|
38
|
+
if not self._double_wide:
|
|
39
|
+
self._windows, self._deltaU_data = self._pair_bidirectionalData(
|
|
40
|
+
self._read_fepout(forward_file),
|
|
41
|
+
self._read_fepout(backward_file)
|
|
42
|
+
)
|
|
43
|
+
else:
|
|
44
|
+
self._windows, self._deltaU_data = self._read_double_wide_fepout(forward_file)
|
|
45
|
+
|
|
46
|
+
if len(self._windows) != len(self._deltaU_data):
|
|
47
|
+
raise RuntimeError('Internal numbers of windows and deltaU do not match! This is a bug!')
|
|
48
|
+
|
|
49
|
+
def _read_fepout(self, fepout_file: str) -> Tuple[List[Tuple[float, float]], List[NDArray]]:
|
|
50
|
+
""" Read an NAMD fepout file. Return the window and deltaU information
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
fepout_file (str): the path of the fepout file
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Tuple[List[Tuple[float, float]], List[NDArray]]: List[Tuple[float, float]], recording
|
|
57
|
+
the boundaries of each window, and
|
|
58
|
+
List[NDArray], recording the deltaU
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
windows = []
|
|
62
|
+
deltaU = []
|
|
63
|
+
with open(fepout_file, 'r') as input_fepout:
|
|
64
|
+
while True:
|
|
65
|
+
line = input_fepout.readline()
|
|
66
|
+
if not line:
|
|
67
|
+
break
|
|
68
|
+
|
|
69
|
+
if line.startswith('#NEW FEP WINDOW:'):
|
|
70
|
+
splitedLine = line.strip().split()
|
|
71
|
+
windows.append((float(splitedLine[6]), float(splitedLine[8])))
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
if line.startswith('#STARTING COLLECTION'):
|
|
75
|
+
# collecting deltaU
|
|
76
|
+
deltaU_per_window = []
|
|
77
|
+
while True:
|
|
78
|
+
line = input_fepout.readline()
|
|
79
|
+
if line.startswith('FepEnergy:'):
|
|
80
|
+
splitedLine = line.strip().split()
|
|
81
|
+
deltaU_per_window.append(float(splitedLine[6]))
|
|
82
|
+
else:
|
|
83
|
+
deltaU.append(np.array(deltaU_per_window))
|
|
84
|
+
break
|
|
85
|
+
|
|
86
|
+
return windows, deltaU
|
|
87
|
+
|
|
88
|
+
def _read_double_wide_fepout(self, fepout_file: str) -> Tuple[List[Tuple[float, float]], List[Tuple[NDArray, NDArray]]]:
|
|
89
|
+
""" Read an NAMD double-wide fepout file. Return the window and bidirectional deltaU information
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
fepout_file (str): the path of the fepout file
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Tuple[List[Tuple[float, float]], List[Tuple[NDArray, NDArray]]]: recording the boundary and
|
|
96
|
+
deltaU of each window of
|
|
97
|
+
bidirectional simulations
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
windows = []
|
|
101
|
+
deltaU_forward = []
|
|
102
|
+
deltaU_backward = []
|
|
103
|
+
with open(fepout_file, 'r') as input_fepout:
|
|
104
|
+
# The first window samples forward only, the last window backward only
|
|
105
|
+
first_window = True
|
|
106
|
+
last_window = False
|
|
107
|
+
while True:
|
|
108
|
+
line = input_fepout.readline()
|
|
109
|
+
if not line:
|
|
110
|
+
break
|
|
111
|
+
|
|
112
|
+
if line.startswith('#NEW FEP WINDOW: LAMBDA SET TO 1') and windows != []:
|
|
113
|
+
last_window = True
|
|
114
|
+
if line.startswith('#NEW FEP WINDOW: LAMBDA SET TO 0 ') and windows != []:
|
|
115
|
+
last_window = True
|
|
116
|
+
|
|
117
|
+
if not last_window:
|
|
118
|
+
if line.startswith('#NEW FEP WINDOW:'):
|
|
119
|
+
splitedLine = line.strip().split()
|
|
120
|
+
windows.append((float(splitedLine[6]), float(splitedLine[8])))
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
if line.startswith('#STARTING COLLECTION'):
|
|
124
|
+
# collecting deltaU
|
|
125
|
+
deltaU_forward_per_window = []
|
|
126
|
+
deltaU_backward_per_window = []
|
|
127
|
+
while True:
|
|
128
|
+
line = input_fepout.readline()
|
|
129
|
+
if line.startswith('FepEnergy:'):
|
|
130
|
+
splitedLine = line.strip().split()
|
|
131
|
+
if not last_window:
|
|
132
|
+
deltaU_forward_per_window.append(float(splitedLine[6]))
|
|
133
|
+
else:
|
|
134
|
+
deltaU_backward_per_window.append(float(splitedLine[6]))
|
|
135
|
+
elif line.startswith('FepE_back:'):
|
|
136
|
+
splitedLine = line.strip().split()
|
|
137
|
+
deltaU_backward_per_window.append(float(splitedLine[6]))
|
|
138
|
+
else:
|
|
139
|
+
if deltaU_forward_per_window != []:
|
|
140
|
+
deltaU_forward.append(np.array(deltaU_forward_per_window))
|
|
141
|
+
if deltaU_backward_per_window != []:
|
|
142
|
+
deltaU_backward.append(np.array(deltaU_backward_per_window))
|
|
143
|
+
break
|
|
144
|
+
|
|
145
|
+
if line.startswith('#Free energy change for lambda window'):
|
|
146
|
+
first_window = False
|
|
147
|
+
last_window = False
|
|
148
|
+
|
|
149
|
+
if len(deltaU_forward) != len(deltaU_backward):
|
|
150
|
+
raise RuntimeError('Forward and backward data do not match!')
|
|
151
|
+
|
|
152
|
+
deltaU = []
|
|
153
|
+
for i in range(len(deltaU_forward)):
|
|
154
|
+
deltaU.append((np.array(deltaU_forward[i]), np.array(deltaU_backward[i])))
|
|
155
|
+
|
|
156
|
+
return windows, deltaU
|
|
157
|
+
|
|
158
|
+
def _pair_bidirectionalData(
|
|
159
|
+
self,
|
|
160
|
+
forward_data: Tuple[List[Tuple[float, float]], List[NDArray]],
|
|
161
|
+
backward_data: Tuple[List[Tuple[float, float]], List[NDArray]]
|
|
162
|
+
) -> Tuple[List[Tuple[float, float]], List[Tuple[NDArray, NDArray]]]:
|
|
163
|
+
""" pair data from bidirectional simulations
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
forward_data (Tuple[List[Tuple[float, float]], List[NDArray]]): data from forward simulation
|
|
167
|
+
backward_data (Tuple[List[Tuple[float, float]], List[NDArray]]): data from backward simulation
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Tuple[List[Tuple[float, float]], List[Tuple[NDArray, NDArray]]]: recording the boundary and
|
|
171
|
+
deltaU of each window of
|
|
172
|
+
bidirectional simulations
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
merged_data = []
|
|
176
|
+
|
|
177
|
+
for i in range(len(forward_data[0])):
|
|
178
|
+
for j in range(len(backward_data[0])):
|
|
179
|
+
if forward_data[0][i][0] == backward_data[0][j][1] and \
|
|
180
|
+
forward_data[0][i][1] == backward_data[0][j][0]:
|
|
181
|
+
merged_data.append((forward_data[1][i], backward_data[1][j]))
|
|
182
|
+
break
|
|
183
|
+
else:
|
|
184
|
+
raise RuntimeError('Error! the forward and backward files do not match!')
|
|
185
|
+
|
|
186
|
+
return forward_data[0], merged_data
|
|
187
|
+
|
|
188
|
+
def get_data(self) -> Tuple[List[Tuple[float, float]], List[Tuple[NDArray, NDArray]]]:
|
|
189
|
+
""" return the boundary and deltaU of each window
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Tuple[List[Tuple[float, float]], List[Tuple[NDArray, NDArray]]]: recording the boundary and
|
|
193
|
+
deltaU of each window of
|
|
194
|
+
bidirectional simulations
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
return self._windows, self._deltaU_data
|
|
198
|
+
|
|
199
|
+
class ColvarsParser:
|
|
200
|
+
""" parse Colvars cvtrj files and get the necessary data
|
|
201
|
+
"""
|
|
202
|
+
def __init__(self,
|
|
203
|
+
cvtrj_file: str,
|
|
204
|
+
step_per_window: int,
|
|
205
|
+
equilibration_per_window: int,
|
|
206
|
+
force_constants: List[int],
|
|
207
|
+
centers: List[int],
|
|
208
|
+
lambda_list: List[float]) -> None:
|
|
209
|
+
|
|
210
|
+
self._windows, self._deltaU_data = self._read_double_wide_cvtrj(
|
|
211
|
+
cvtrj_file,
|
|
212
|
+
step_per_window,
|
|
213
|
+
equilibration_per_window,
|
|
214
|
+
force_constants,
|
|
215
|
+
centers,
|
|
216
|
+
lambda_list
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
if len(self._windows) != len(self._deltaU_data):
|
|
220
|
+
print(len(self._windows))
|
|
221
|
+
print(len(self._deltaU_data))
|
|
222
|
+
raise RuntimeError('Internal numbers of windows and deltaU do not match! This is a bug!')
|
|
223
|
+
|
|
224
|
+
self._restaint_contribution = \
|
|
225
|
+
self._alchemicalRestraintContributionBulk(centers[0], centers[3], centers[5], *force_constants)
|
|
226
|
+
|
|
227
|
+
def _read_double_wide_cvtrj(
|
|
228
|
+
self,
|
|
229
|
+
cvtrj_file: str,
|
|
230
|
+
step_per_window: int,
|
|
231
|
+
equilibration_per_window: int,
|
|
232
|
+
force_constants: List[int],
|
|
233
|
+
centers: List[int],
|
|
234
|
+
lambda_list: List[float]
|
|
235
|
+
) -> Tuple[List[Tuple[float, float]], List[Tuple[NDArray, NDArray]]]:
|
|
236
|
+
""" Read an Colvars cvtrj file and regard it as double-wide free-energy calculation.
|
|
237
|
+
Return the window and bidirectional deltaU information.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
cvtrj_file (str): the path of the fepout file
|
|
241
|
+
step_per_window (int): total steps of a window
|
|
242
|
+
equilibration_per_window (int): steps of equilibration of a window
|
|
243
|
+
force_constants (List[int]) force constants of each CV.
|
|
244
|
+
centers (List[int]): the center (lambda=1) of each CV. The first N CVs are considered.
|
|
245
|
+
lambda_list (List[float]): Lambda schedule [0, ..., 1] or [1, ..., 0]
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Tuple[List[Tuple[float, float]], List[Tuple[NDArray, NDArray]]]: recording the boundary and
|
|
249
|
+
deltaU of each window of
|
|
250
|
+
bidirectional simulations
|
|
251
|
+
"""
|
|
252
|
+
|
|
253
|
+
assert len(force_constants) == len(centers), "Error, The lengths of force_constants of centers are not equal!"
|
|
254
|
+
|
|
255
|
+
force_constants = np.array(force_constants)
|
|
256
|
+
centers = np.array(centers)
|
|
257
|
+
lambda_list = np.array(lambda_list)
|
|
258
|
+
|
|
259
|
+
windows = []
|
|
260
|
+
for i in range(len(lambda_list) - 1):
|
|
261
|
+
windows.append((lambda_list[i], lambda_list[i + 1]))
|
|
262
|
+
|
|
263
|
+
deltaU_forward = []
|
|
264
|
+
deltaU_backward = []
|
|
265
|
+
|
|
266
|
+
num_CVs = len(force_constants)
|
|
267
|
+
num_windows = len(lambda_list)
|
|
268
|
+
|
|
269
|
+
with open(cvtrj_file, 'r') as input_fepout:
|
|
270
|
+
# The first window samples forward only, the last window backward only
|
|
271
|
+
window_index = 0
|
|
272
|
+
|
|
273
|
+
while True:
|
|
274
|
+
line = input_fepout.readline()
|
|
275
|
+
if not line:
|
|
276
|
+
break
|
|
277
|
+
if line.startswith("#"):
|
|
278
|
+
continue
|
|
279
|
+
splitedLine = line.strip().split()
|
|
280
|
+
step = int(splitedLine[0])
|
|
281
|
+
|
|
282
|
+
if step % step_per_window == 0:
|
|
283
|
+
# collecting deltaU
|
|
284
|
+
deltaU_forward_per_window = []
|
|
285
|
+
deltaU_backward_per_window = []
|
|
286
|
+
|
|
287
|
+
while True:
|
|
288
|
+
line = input_fepout.readline()
|
|
289
|
+
if not line:
|
|
290
|
+
break
|
|
291
|
+
if line.startswith("#"):
|
|
292
|
+
continue
|
|
293
|
+
splitedLine = line.strip().split()
|
|
294
|
+
step = int(splitedLine[0])
|
|
295
|
+
|
|
296
|
+
if step % step_per_window == 0:
|
|
297
|
+
if window_index == 0:
|
|
298
|
+
deltaU_forward.append(deltaU_forward_per_window)
|
|
299
|
+
elif window_index == num_windows - 1:
|
|
300
|
+
deltaU_backward.append(deltaU_backward_per_window)
|
|
301
|
+
else:
|
|
302
|
+
deltaU_forward.append(deltaU_forward_per_window)
|
|
303
|
+
deltaU_backward.append(deltaU_backward_per_window)
|
|
304
|
+
window_index += 1
|
|
305
|
+
break
|
|
306
|
+
|
|
307
|
+
# equilibration
|
|
308
|
+
if step < window_index * step_per_window + equilibration_per_window:
|
|
309
|
+
continue
|
|
310
|
+
else:
|
|
311
|
+
dis = abs(np.array(splitedLine[1:1+num_CVs]).astype(float) - centers)
|
|
312
|
+
dis[dis>180] -= 360
|
|
313
|
+
if window_index == 0:
|
|
314
|
+
deltaU_forward_per_window.append(0.5 * np.sum((lambda_list[window_index + 1] - lambda_list[window_index]) * force_constants * (dis**2)))
|
|
315
|
+
elif window_index == num_windows - 1:
|
|
316
|
+
deltaU_backward_per_window.append(0.5 * np.sum((lambda_list[window_index - 1] - lambda_list[window_index]) * force_constants * (dis**2)))
|
|
317
|
+
else:
|
|
318
|
+
deltaU_forward_per_window.append(0.5 * np.sum((lambda_list[window_index + 1] - lambda_list[window_index]) * force_constants * (dis**2)))
|
|
319
|
+
deltaU_backward_per_window.append(0.5 * np.sum((lambda_list[window_index - 1] - lambda_list[window_index]) * force_constants * (dis**2)))
|
|
320
|
+
|
|
321
|
+
if len(deltaU_forward) != len(deltaU_backward):
|
|
322
|
+
raise RuntimeError('Forward and backward data do not match!')
|
|
323
|
+
|
|
324
|
+
deltaU = []
|
|
325
|
+
for i in range(len(deltaU_forward)):
|
|
326
|
+
deltaU.append((np.array(deltaU_forward[i]), np.array(deltaU_backward[i])))
|
|
327
|
+
|
|
328
|
+
return windows, deltaU
|
|
329
|
+
|
|
330
|
+
def get_data(self) -> Tuple[List[Tuple[float, float]], List[Tuple[NDArray, NDArray]]]:
|
|
331
|
+
return self._windows, self._deltaU_data
|
|
332
|
+
|
|
333
|
+
def get_restraint_contribution(self) -> float:
|
|
334
|
+
return self._restaint_contribution
|
|
335
|
+
|
|
336
|
+
def _alchemicalRestraintContributionBulk(
|
|
337
|
+
self, eulerTheta, polarTheta, R,
|
|
338
|
+
forceConstantTheta=0.1, forceConstantPhi=0.1, forceConstantPsi=0.1,
|
|
339
|
+
forceConstanttheta=0.1, forceConstantphi=0.1, forceConstantR=10
|
|
340
|
+
):
|
|
341
|
+
"""contribution of (standard concentration corrected) rotational
|
|
342
|
+
and orienetational restraints in the unbounded state
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
eulerTheta (float): restraining center of the Euler angle theta
|
|
346
|
+
polarTheta (float): restraining center of the polar angle theta
|
|
347
|
+
R (float): restraining center of anger R
|
|
348
|
+
forceConstantTheta (float): restraining force constant for euler Theta. Defaults to 0.1.
|
|
349
|
+
forceConstantPhi (float, optional): restraining force constant for euler Phi. Defaults to 0.1.
|
|
350
|
+
forceConstantPsi (float, optional): restraining force constant for euler Psi. Defaults to 0.1.
|
|
351
|
+
forceConstanttheta (float, optional): restraining force constant for polar theta. Defaults to 0.1.
|
|
352
|
+
forceConstantphi (float, optional): restraining force constant for polar phi. Defaults to 0.1.
|
|
353
|
+
forceConstantR (int, optional): restraining force constant for distance R. Defaults to 10.
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
float: contribution of the geometric restraint in the unbound state
|
|
357
|
+
"""
|
|
358
|
+
|
|
359
|
+
# degrees to rad
|
|
360
|
+
eulerTheta = math.radians(eulerTheta + 90)
|
|
361
|
+
polarTheta = math.radians(polarTheta)
|
|
362
|
+
forceConstantTheta *= (180 / math.pi)**2
|
|
363
|
+
forceConstantPhi *= (180 / math.pi)**2
|
|
364
|
+
forceConstantPsi *= (180 / math.pi)**2
|
|
365
|
+
forceConstanttheta *= (180 / math.pi)**2
|
|
366
|
+
forceConstantphi *= (180 / math.pi)**2
|
|
367
|
+
|
|
368
|
+
contribution = BOLTZMANN * 300 * math.log(
|
|
369
|
+
8 * (math.pi**2) * CSTAR / ((R**2) * math.sin(eulerTheta) * math.sin(polarTheta)) * \
|
|
370
|
+
math.sqrt(forceConstantTheta * forceConstantPhi * forceConstantPsi * forceConstanttheta * \
|
|
371
|
+
forceConstantphi * forceConstantR ) / ((2 * math.pi * BOLTZMANN * 300)**3)
|
|
372
|
+
)
|
|
373
|
+
return contribution
|
|
374
|
+
|
|
375
|
+
class FEPAnalyzer:
|
|
376
|
+
""" Analyze FEP simulations
|
|
377
|
+
"""
|
|
378
|
+
|
|
379
|
+
def __init__(
|
|
380
|
+
self,
|
|
381
|
+
window_boundaries: List[Tuple[float, float]],
|
|
382
|
+
deltaU_data: List[Tuple[NDArray, NDArray]],
|
|
383
|
+
temperature: float,
|
|
384
|
+
) -> None:
|
|
385
|
+
"""_summary_
|
|
386
|
+
|
|
387
|
+
Args:
|
|
388
|
+
window_boundaries (List[Tuple[float, float]]): boundaries of each window
|
|
389
|
+
deltaU_data (List[Tuple[NDArray, NDArray]]): deltaU for forward and backward simulations
|
|
390
|
+
of each window. The two deltaU should be opposite
|
|
391
|
+
numbers.
|
|
392
|
+
temperature (float): temperature of the simulation
|
|
393
|
+
"""
|
|
394
|
+
self._windows, self._deltaU_data = window_boundaries, deltaU_data
|
|
395
|
+
self._temperature = temperature
|
|
396
|
+
if len(self._windows) != len(self._deltaU_data):
|
|
397
|
+
raise RuntimeError('Internal numbers of windows and deltaU do not match! This is a bug!')
|
|
398
|
+
|
|
399
|
+
def MergeData(
|
|
400
|
+
self,
|
|
401
|
+
window_boundaries: List[Tuple[float, float]],
|
|
402
|
+
deltaU_data: List[Tuple[NDArray, NDArray]]
|
|
403
|
+
) -> bool:
|
|
404
|
+
""" Merge the exist dU with another one. Used in FEP + Colvars_FEP tasks.
|
|
405
|
+
window boundaries and the number of deltaU in a window must be the same
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
window_boundaries (List[Tuple[float, float]]): boundaries of each window
|
|
409
|
+
deltaU_data (List[Tuple[NDArray, NDArray]]): deltaU for forward and backward simulations
|
|
410
|
+
of each window. The two deltaU should be opposite
|
|
411
|
+
numbers.
|
|
412
|
+
|
|
413
|
+
Returns:
|
|
414
|
+
bool: Whether merge is successful
|
|
415
|
+
"""
|
|
416
|
+
if len(window_boundaries) != len(self._windows):
|
|
417
|
+
print(1)
|
|
418
|
+
return False
|
|
419
|
+
|
|
420
|
+
#for i in range(len(window_boundaries)):
|
|
421
|
+
# print(f'FEP: {self._windows[i]} Colvars: {window_boundaries[i]}')
|
|
422
|
+
# print(f'FEP: {len(deltaU_data[i][0])} Colvars: {len(self._deltaU_data[i][0])}')
|
|
423
|
+
|
|
424
|
+
for i in range(len(window_boundaries)):
|
|
425
|
+
if (len(deltaU_data[i][0]) - 1) != len(self._deltaU_data[i][0]) \
|
|
426
|
+
and (len(deltaU_data[i][0]) - 1) != len(self._deltaU_data[i][0]) / 2 \
|
|
427
|
+
and len(deltaU_data[i][0]) != len(self._deltaU_data[i][0]) \
|
|
428
|
+
and len(deltaU_data[i][0]) != len(self._deltaU_data[i][0]) / 2:
|
|
429
|
+
return False
|
|
430
|
+
if (len(deltaU_data[i][1]) - 1) != len(self._deltaU_data[i][1]) \
|
|
431
|
+
and (len(deltaU_data[i][1]) - 1) != len(self._deltaU_data[i][1]) / 2 \
|
|
432
|
+
and len(deltaU_data[i][1]) != len(self._deltaU_data[i][1]) \
|
|
433
|
+
and len(deltaU_data[i][1]) != len(self._deltaU_data[i][1]) / 2:
|
|
434
|
+
return False
|
|
435
|
+
if (len(deltaU_data[i][0]) - 1) == len(self._deltaU_data[i][0]):
|
|
436
|
+
temp_forward = self._deltaU_data[i][0] + deltaU_data[i][0][:-1]
|
|
437
|
+
elif (len(deltaU_data[i][0]) - 1) == len(self._deltaU_data[i][0]) / 2:
|
|
438
|
+
temp_forward = self._deltaU_data[i][0][1::2]
|
|
439
|
+
temp_forward += deltaU_data[i][0][:-1]
|
|
440
|
+
elif len(deltaU_data[i][0]) == len(self._deltaU_data[i][0]):
|
|
441
|
+
temp_forward = self._deltaU_data[i][0] + deltaU_data[i][0]
|
|
442
|
+
elif len(deltaU_data[i][0]) == len(self._deltaU_data[i][0]) / 2:
|
|
443
|
+
temp_forward = self._deltaU_data[i][0][1::2]
|
|
444
|
+
temp_forward += deltaU_data[i][0]
|
|
445
|
+
|
|
446
|
+
if (len(deltaU_data[i][1]) - 1) == len(self._deltaU_data[i][1]):
|
|
447
|
+
temp_backward = self._deltaU_data[i][1] + deltaU_data[i][1][:-1]
|
|
448
|
+
elif (len(deltaU_data[i][1]) - 1) == len(self._deltaU_data[i][1]) / 2:
|
|
449
|
+
temp_backward = self._deltaU_data[i][1][::2]
|
|
450
|
+
temp_backward += deltaU_data[i][1][:-1]
|
|
451
|
+
elif len(deltaU_data[i][1]) == len(self._deltaU_data[i][1]):
|
|
452
|
+
temp_backward = self._deltaU_data[i][1] + deltaU_data[i][1]
|
|
453
|
+
elif len(deltaU_data[i][1]) == len(self._deltaU_data[i][1]) / 2:
|
|
454
|
+
temp_backward = self._deltaU_data[i][1][::2]
|
|
455
|
+
temp_backward += deltaU_data[i][1]
|
|
456
|
+
self._deltaU_data[i] = (temp_forward, temp_backward)
|
|
457
|
+
|
|
458
|
+
return True
|
|
459
|
+
|
|
460
|
+
def FEP_free_energy(self) -> Tuple[List[Tuple[float, float]], List[NDArray], List[NDArray]]:
|
|
461
|
+
""" Calculate and return the free-energy change using the FEP equation
|
|
462
|
+
|
|
463
|
+
Returns:
|
|
464
|
+
Tuple[List[Tuple[float, float]], List[NDArray], List[NDArray]]: window boundaries, free energies and errors
|
|
465
|
+
"""
|
|
466
|
+
|
|
467
|
+
free_energies = []
|
|
468
|
+
errors = []
|
|
469
|
+
for i in range(len(self._windows)):
|
|
470
|
+
forward_free_energy = -BOLTZMANN * self._temperature * \
|
|
471
|
+
np.log(np.mean(np.exp(-self._deltaU_data[i][0] / (BOLTZMANN * self._temperature))))
|
|
472
|
+
backward_free_energy = -BOLTZMANN * self._temperature * \
|
|
473
|
+
np.log(np.mean(np.exp(-self._deltaU_data[i][1] / (BOLTZMANN * self._temperature))))
|
|
474
|
+
free_energies.append((forward_free_energy - backward_free_energy) / 2)
|
|
475
|
+
errors.append(np.abs(forward_free_energy + backward_free_energy) / np.sqrt(2))
|
|
476
|
+
return self._windows, free_energies, errors
|
|
477
|
+
|
|
478
|
+
def BAR_free_energy(
|
|
479
|
+
self,
|
|
480
|
+
tolerance: float = 1e-6,
|
|
481
|
+
block_size: int = 20,
|
|
482
|
+
n_bootstrap: int = 20,
|
|
483
|
+
) -> Tuple[List[Tuple[float, float]], List[NDArray], List[NDArray]]:
|
|
484
|
+
""" Calculate and return the free-energy change using the BAR estimator
|
|
485
|
+
|
|
486
|
+
Args:
|
|
487
|
+
tolerance (float): tolerance of the SCF. Default to 1e-6.
|
|
488
|
+
block_size (int): the size of the block in block bootstrap. Default to 10.
|
|
489
|
+
n_bootstrap (int): number of bootstrap samples. Default to 20.
|
|
490
|
+
|
|
491
|
+
Returns:
|
|
492
|
+
Tuple[List[Tuple[float, float]], List[NDArray], List[NDArray]]: window boundaries, free energies and errors
|
|
493
|
+
"""
|
|
494
|
+
|
|
495
|
+
free_energies = []
|
|
496
|
+
errors = []
|
|
497
|
+
for i in range(len(self._windows)):
|
|
498
|
+
dA = self._BAR_estimator(self._deltaU_data[i], tolerance)
|
|
499
|
+
err = self._BAR_error_estimator(self._deltaU_data[i], tolerance, block_size, n_bootstrap)
|
|
500
|
+
|
|
501
|
+
free_energies.append(dA)
|
|
502
|
+
errors.append(err)
|
|
503
|
+
return self._windows, free_energies, errors
|
|
504
|
+
|
|
505
|
+
def Window_boundaries(self) -> List[Tuple[float, float]]:
|
|
506
|
+
""" Get the boundaries of windows
|
|
507
|
+
|
|
508
|
+
Returns:
|
|
509
|
+
List[Tuple[float, float]]: windows boundaries
|
|
510
|
+
"""
|
|
511
|
+
return self._windows
|
|
512
|
+
|
|
513
|
+
def _BAR_estimator(self, deltaU: Tuple[NDArray, NDArray], tolerance: float = 1e-6) -> float:
|
|
514
|
+
""" Estimate the free energy of a window using the BAR estimator
|
|
515
|
+
|
|
516
|
+
Args:
|
|
517
|
+
Tuple[NDArray, NDArray]: deltaU data of forward and backward simulations
|
|
518
|
+
tolerance (float): tolerance of the SCF. Default to 1e-6.
|
|
519
|
+
|
|
520
|
+
Returns:
|
|
521
|
+
float: free energy change
|
|
522
|
+
"""
|
|
523
|
+
|
|
524
|
+
def fermi(x):
|
|
525
|
+
return 1 / (1 + np.exp(x))
|
|
526
|
+
|
|
527
|
+
beta = 1 / (BOLTZMANN * self._temperature)
|
|
528
|
+
c = 0
|
|
529
|
+
# BAR estimator
|
|
530
|
+
exp_beta_dA = np.mean(fermi(beta * (deltaU[0] - c))) / np.mean(fermi(beta * (deltaU[1] + c)))
|
|
531
|
+
dA = np.log(exp_beta_dA) / (-beta) + c
|
|
532
|
+
|
|
533
|
+
while np.abs(c - dA) > tolerance:
|
|
534
|
+
c = dA
|
|
535
|
+
# BAR estimator
|
|
536
|
+
exp_beta_dA = np.mean(fermi(beta * (deltaU[0] - c))) / np.mean(fermi(beta * (deltaU[1] + c)))
|
|
537
|
+
dA = np.log(exp_beta_dA) / (-beta) + c
|
|
538
|
+
|
|
539
|
+
return dA
|
|
540
|
+
|
|
541
|
+
def _BAR_error_estimator(
|
|
542
|
+
self,
|
|
543
|
+
deltaU: Tuple[NDArray, NDArray],
|
|
544
|
+
tolerance: float = 1e-6,
|
|
545
|
+
block_size: int = 20,
|
|
546
|
+
n_bootstrap: int = 20
|
|
547
|
+
) -> float:
|
|
548
|
+
""" Estimate the error of the free energy estimate of a window
|
|
549
|
+
using the BAR estimator and block bootstrap method
|
|
550
|
+
|
|
551
|
+
Args:
|
|
552
|
+
Tuple[NDArray, NDArray]: deltaU data of forward and backward simulations
|
|
553
|
+
tolerance (float): tolerance of the SCF. Default to 1e-6.
|
|
554
|
+
block_size (int): the size of the block in block bootstrap. Default to 10.
|
|
555
|
+
n_bootstrap (int): number of bootstrap samples. Default to 20.
|
|
556
|
+
|
|
557
|
+
Returns:
|
|
558
|
+
float: error of the free-energy estimator
|
|
559
|
+
"""
|
|
560
|
+
|
|
561
|
+
forward_size = len(deltaU[0])
|
|
562
|
+
backward_size = len(deltaU[1])
|
|
563
|
+
bootstrap_samples = int(np.max((forward_size, backward_size)) / block_size)
|
|
564
|
+
|
|
565
|
+
if bootstrap_samples < 1:
|
|
566
|
+
raise RuntimeError('Error! block_size larger than sample size!')
|
|
567
|
+
|
|
568
|
+
# block bootstrap
|
|
569
|
+
estimates = np.zeros(n_bootstrap)
|
|
570
|
+
|
|
571
|
+
for i in range(n_bootstrap):
|
|
572
|
+
forward_bootstrap = np.zeros(bootstrap_samples * block_size, dtype=int)
|
|
573
|
+
for idx, j in enumerate(np.random.randint(0, forward_size - block_size - 1, bootstrap_samples)):
|
|
574
|
+
forward_bootstrap[idx*block_size:idx*block_size+block_size] = j + np.arange(block_size)
|
|
575
|
+
|
|
576
|
+
backward_bootstrap = np.zeros(bootstrap_samples * block_size, dtype=int)
|
|
577
|
+
for idx, j in enumerate(np.random.randint(0, backward_size - block_size - 1, bootstrap_samples)):
|
|
578
|
+
backward_bootstrap[idx*block_size:idx*block_size+block_size] = j + np.arange(block_size)
|
|
579
|
+
|
|
580
|
+
estimates[i] = self._BAR_estimator(
|
|
581
|
+
(deltaU[0][forward_bootstrap], deltaU[1][backward_bootstrap]),
|
|
582
|
+
tolerance
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
return np.std(estimates)
|
BFEE2/version.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!python
|
|
2
|
+
###################################################
|
|
3
|
+
## binding free energy estimator (BFEE) v2.x
|
|
4
|
+
## by Haohao Fu (fhh2626_at_gmail.com)
|
|
5
|
+
##
|
|
6
|
+
## this is a completely rewritten version of BFEE
|
|
7
|
+
##
|
|
8
|
+
###################################################
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
from PySide6.QtWidgets import QApplication
|
|
12
|
+
from PySide6.QtGui import QAction
|
|
13
|
+
import BFEE2.gui as gui
|
|
14
|
+
|
|
15
|
+
if __name__ == '__main__':
|
|
16
|
+
|
|
17
|
+
app = QApplication(sys.argv)
|
|
18
|
+
ex = gui.mainUI()
|
|
19
|
+
sys.exit(app.exec())
|