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
@@ -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,4 @@
1
+ __VERSION__ = '3.1.1.post1'
2
+ __NAMD_VERSION__ = '3.0.1'
3
+ __GMX_VERSION__ = '2024'
4
+
@@ -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())