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.

Files changed (65) 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 +2136 -0
  9. BFEE2/inputGenerator.py +2857 -0
  10. BFEE2/postTreatment.py +502 -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 +70 -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 +70 -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 +70 -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 +70 -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 +70 -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 +70 -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 +70 -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 +69 -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 +58 -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 +70 -0
  42. BFEE2/templates_gromacs/008_eq.generate_tpr_sh.template +31 -0
  43. BFEE2/templates_gromacs/BFEEGromacs.py +1244 -0
  44. BFEE2/templates_gromacs/__init__.py +0 -0
  45. BFEE2/templates_gromacs/find_min_max.awk +27 -0
  46. BFEE2/templates_namd/__init__.py +0 -0
  47. BFEE2/templates_namd/configTemplate.py +1094 -0
  48. BFEE2/templates_namd/fep.tcl +299 -0
  49. BFEE2/templates_namd/scriptTemplate.py +149 -0
  50. BFEE2/templates_namd/solvate.tcl +9 -0
  51. BFEE2/templates_namd/solvate_mem.tcl +9 -0
  52. BFEE2/templates_namd/updateCenters.py +311 -0
  53. BFEE2/templates_readme/Readme_Gromacs_Geometrical.txt +25 -0
  54. BFEE2/templates_readme/Readme_NAMD_Alchemical.txt +20 -0
  55. BFEE2/templates_readme/Readme_NAMD_Geometrical.txt +34 -0
  56. BFEE2/templates_readme/__init__.py +1 -0
  57. BFEE2/third_party/__init__.py +0 -0
  58. BFEE2/third_party/py_bar.py +335 -0
  59. BFEE2/version.py +2 -0
  60. bfee2-2.5.0.data/scripts/BFEE2Gui.py +18 -0
  61. bfee2-2.5.0.dist-info/LICENSE +677 -0
  62. bfee2-2.5.0.dist-info/METADATA +76 -0
  63. bfee2-2.5.0.dist-info/RECORD +65 -0
  64. bfee2-2.5.0.dist-info/WHEEL +5 -0
  65. bfee2-2.5.0.dist-info/top_level.txt +1 -0
BFEE2/gui.py ADDED
@@ -0,0 +1,2136 @@
1
+ # the GUI of new BFEE
2
+
3
+ import os
4
+ import shutil
5
+ import sys
6
+ import webbrowser
7
+
8
+ import numpy as np
9
+ # use appdirs to manage persistent configuration
10
+ from appdirs import user_config_dir
11
+ from PySide2 import QtCore
12
+ from PySide2.QtGui import QFont, QIcon
13
+ from PySide2.QtWidgets import (QAction, QApplication, QCheckBox, QComboBox,
14
+ QFileDialog, QGridLayout, QGroupBox,
15
+ QHBoxLayout, QLabel, QLineEdit, QListWidget,
16
+ QMainWindow, QMessageBox, QPushButton,
17
+ QSplitter, QTabWidget, QToolBar, QVBoxLayout,
18
+ QWidget, QSpacerItem)
19
+
20
+ import BFEE2.inputGenerator as inputGenerator
21
+ import BFEE2.postTreatment as postTreatment
22
+ import BFEE2.templates_gromacs.BFEEGromacs as BFEEGromacs
23
+ from BFEE2.commonTools import commonSlots, fileParser, ploter
24
+
25
+ try:
26
+ import importlib.resources as pkg_resources
27
+ except ImportError:
28
+ # Try backported to PY<37 `importlib_resources`.
29
+ import importlib_resources as pkg_resources
30
+
31
+ import BFEE2.version
32
+ from BFEE2 import doc
33
+
34
+ __PROGRAM_NAME__ = f'BFEEstimator v{BFEE2.version.__VERSION__}'
35
+ __NAMD_VERSION__ = f'v{BFEE2.version.__NAMD_VERSION__}'
36
+
37
+ class mainSettings(QWidget):
38
+ """settings in the menubar
39
+ set pathes of third-party softwares (VMD, gmx and tleap)
40
+ """
41
+
42
+ def __init__(self):
43
+ super().__init__()
44
+ self.config_dir = user_config_dir('BFEE2', 'chinfo')
45
+ # test if config directory exists
46
+ if not os.path.exists(self.config_dir):
47
+ # create it if not exists
48
+ os.makedirs(self.config_dir)
49
+ self._initUI()
50
+ self._initSingalsSlots()
51
+ self.setWindowTitle('Settings')
52
+ self._readConfig()
53
+ #self.setGeometry(0,0,0,0)
54
+ #self.show()
55
+
56
+ def _initUI(self):
57
+ """settings GUI
58
+ """
59
+
60
+ self.mainLayout = QVBoxLayout()
61
+
62
+ self.thirdPartySoftware = QGroupBox('Third party software:')
63
+ self.thirdPartySoftwareLayout = QVBoxLayout()
64
+
65
+ # settings grid
66
+ self.settingsGridLayout = QGridLayout()
67
+
68
+ # vmd
69
+ self.vmdLabel = QLabel('VMD:')
70
+ self.vmdLineEdit = QLineEdit()
71
+ self.vmdButton = QPushButton('Browse')
72
+ self.settingsGridLayout.addWidget(self.vmdLabel, 0, 0)
73
+ self.settingsGridLayout.addWidget(self.vmdLineEdit, 0, 1)
74
+ self.settingsGridLayout.addWidget(self.vmdButton, 0, 2)
75
+
76
+ # gmx
77
+ #self.gromacsLayout = QHBoxLayout()
78
+ #self.gromacsLabel = QLabel('Gromacs:')
79
+ #self.gromacsLineEdit = QLineEdit()
80
+ #self.gromacsButton = QPushButton('Browse')
81
+ #self.settingsGridLayout.addWidget(self.gromacsLabel, 1, 0)
82
+ #self.settingsGridLayout.addWidget(self.gromacsLineEdit,1 ,1)
83
+ #self.settingsGridLayout.addWidget(self.gromacsButton, 1, 2)
84
+
85
+ # tleap
86
+ #self.tleapLayout = QHBoxLayout()
87
+ #self.tleapLabel = QLabel('tleap:')
88
+ #self.tleapLineEdit = QLineEdit()
89
+ #self.tleapButton = QPushButton('Browse')
90
+ #self.settingsGridLayout.addWidget(self.tleapLabel, 2, 0)
91
+ #self.settingsGridLayout.addWidget(self.tleapLineEdit, 2, 1)
92
+ #self.settingsGridLayout.addWidget(self.tleapButton, 2, 2)
93
+
94
+ # OK and Cancel
95
+ self.settingsButtonLayout = QHBoxLayout()
96
+ self.settingsOKButton = QPushButton('OK')
97
+ #self.settingsCancelButton = QPushButton('Cancel')
98
+ self.settingsButtonLayout.addWidget(QSplitter())
99
+ self.settingsButtonLayout.addWidget(self.settingsOKButton)
100
+ #self.settingsButtonLayout.addWidget(self.settingsCancelButton)
101
+
102
+ self.thirdPartySoftwareLayout.addLayout(self.settingsGridLayout)
103
+ self.thirdPartySoftwareLayout.addLayout(self.settingsButtonLayout)
104
+
105
+ self.thirdPartySoftware.setLayout(self.thirdPartySoftwareLayout)
106
+ self.mainLayout.addWidget(self.thirdPartySoftware)
107
+ self.setLayout(self.mainLayout)
108
+
109
+ def _initSingalsSlots(self):
110
+ """initialize singals and slots
111
+ """
112
+ self.vmdButton.clicked.connect(commonSlots.openFileDialog('exe', self.vmdLineEdit))
113
+ #self.gromacsButton.clicked.connect(commonSlots.openFileDialog('exe', self.gromacsLineEdit))
114
+ #self.tleapButton.clicked.connect(commonSlots.openFileDialog('exe', self.tleapLineEdit))
115
+ self.settingsOKButton.clicked.connect(self._OKSlot())
116
+
117
+ def _readConfig(self):
118
+ """read the config saving paths for third-party softwares
119
+ """
120
+ if not os.path.exists(f'{self.config_dir}/3rdSoft.ini'):
121
+ return
122
+
123
+ with open(f'{self.config_dir}/3rdSoft.ini', 'r') as cFile:
124
+ line = cFile.readline()
125
+ self.vmdLineEdit.setText(line.strip())
126
+ #line = cFile.readline()
127
+ #self.gromacsLineEdit.setText(line.strip())
128
+ #line = cFile.readline()
129
+ #self.tleapLineEdit.setText(line.strip())
130
+
131
+ def _writeConfig(self):
132
+ """write the config saving paths for third-party softwares
133
+ """
134
+
135
+ with open(f'{self.config_dir}/3rdSoft.ini', 'w') as cFile:
136
+ cFile.write(self.vmdLineEdit.text() + '\n')
137
+ #cFile.write(self.gromacsLineEdit.text() + '\n')
138
+ #cFile.write(self.tleapLineEdit.text() + '\n')
139
+
140
+ def _OKSlot(self):
141
+ """the slot corresponding the OK button
142
+ """
143
+ def f():
144
+ self._writeConfig()
145
+ self.close()
146
+ return f
147
+
148
+ class geometricAdvancedSettings(QWidget):
149
+ """advanced settings for the geometric route
150
+ set pulling direction, non-standard solvent
151
+ and number of stratification windows
152
+ """
153
+
154
+ def __init__(self):
155
+ super().__init__()
156
+ self._initUI()
157
+ self._initSingalsSlots()
158
+ self.setWindowTitle('Geometric advanced settings')
159
+ self.setGeometry(0,0,0,0)
160
+ #self.show()
161
+
162
+ def _initUI(self):
163
+ """initialize UI for the geometric advanced settings
164
+ """
165
+
166
+ self.mainLayout = QVBoxLayout()
167
+
168
+ # user-defined pulling direction
169
+ self.userDefinedDirection = QGroupBox('User-defined separation direction')
170
+ self.userDefinedDirectionLayout = QHBoxLayout()
171
+
172
+ self.userDefinedDirectionLabel = QLabel('Reference:')
173
+ self.userDefinedDirectionLineEdit = QLineEdit()
174
+ self.userDefinedDirectionLayout.addWidget(self.userDefinedDirectionLabel)
175
+ self.userDefinedDirectionLayout.addWidget(self.userDefinedDirectionLineEdit)
176
+
177
+ self.userDefinedDirection.setLayout(self.userDefinedDirectionLayout)
178
+
179
+ # non-standard solvent
180
+ self.nonStandardSolvent = QGroupBox('User-provided large box')
181
+ self.nonStandardSolventLayout = QGridLayout()
182
+
183
+ self.nonStandardSolventPsfLabel = QLabel('psf/parm file:')
184
+ self.nonStandardSolventPsfLineEdit = QLineEdit()
185
+ self.nonStandardSolventPsfButton = QPushButton('Browse')
186
+ self.nonStandardSolventPdbLabel = QLabel('pdb/rst7 file:')
187
+ self.nonStandardSolventPdbLineEdit = QLineEdit()
188
+ self.nonStandardSolventPdbButton = QPushButton('Browse')
189
+ self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPsfLabel, 0, 0)
190
+ self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPsfLineEdit, 0, 1)
191
+ self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPsfButton, 0, 2)
192
+ self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPdbLabel, 1, 0)
193
+ self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPdbLineEdit, 1, 1)
194
+ self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPdbButton, 1, 2)
195
+
196
+ self.nonStandardSolvent.setLayout(self.nonStandardSolventLayout)
197
+
198
+ # stratification
199
+ self.stratification = QGroupBox('Stratification (number of strata)')
200
+ self.stratificationLayout = QGridLayout()
201
+
202
+ self.stratificationRMSDBoundLabel = QLabel('RMSD(Bound):')
203
+ self.stratificationRMSDBoundLineEdit = QLineEdit('1')
204
+ self.stratificationTheta = QLabel('Theta:')
205
+ self.stratificationThetaLineEdit = QLineEdit('1')
206
+ self.stratificationPhiLabel = QLabel('Phi:')
207
+ self.stratificationPhiLineEdit = QLineEdit('1')
208
+ self.stratificationPsiLabel = QLabel('Psi:')
209
+ self.stratificationPsiLineEdit = QLineEdit('1')
210
+ self.stratificationthetaLabel = QLabel('theta:')
211
+ self.stratificationthetaLineEdit = QLineEdit('1')
212
+ self.stratificationphiLabel = QLabel('phi:')
213
+ self.stratificationphiLineEdit = QLineEdit('1')
214
+ self.stratificationRLabel = QLabel('r:')
215
+ self.stratificationRLineEdit = QLineEdit('1')
216
+ self.stratificationRMSDUnboundLabel = QLabel('RMSD(Unbound):')
217
+ self.stratificationRMSDUnboundLineEdit = QLineEdit('1')
218
+
219
+ self.stratificationLayout.addWidget(self.stratificationRMSDBoundLabel, 0, 0)
220
+ self.stratificationLayout.addWidget(self.stratificationRMSDBoundLineEdit, 0, 1)
221
+ self.stratificationLayout.addWidget(self.stratificationTheta, 0, 2)
222
+ self.stratificationLayout.addWidget(self.stratificationThetaLineEdit, 0, 3)
223
+ self.stratificationLayout.addWidget(self.stratificationPhiLabel, 0, 4)
224
+ self.stratificationLayout.addWidget(self.stratificationPhiLineEdit, 0, 5)
225
+ self.stratificationLayout.addWidget(self.stratificationPsiLabel, 0, 6)
226
+ self.stratificationLayout.addWidget(self.stratificationPsiLineEdit, 0, 7)
227
+ self.stratificationLayout.addWidget(self.stratificationthetaLabel, 1, 0)
228
+ self.stratificationLayout.addWidget(self.stratificationthetaLineEdit, 1, 1)
229
+ self.stratificationLayout.addWidget(self.stratificationphiLabel, 1, 2)
230
+ self.stratificationLayout.addWidget(self.stratificationphiLineEdit, 1, 3)
231
+ self.stratificationLayout.addWidget(self.stratificationRLabel, 1, 4)
232
+ self.stratificationLayout.addWidget(self.stratificationRLineEdit, 1, 5)
233
+ self.stratificationLayout.addWidget(self.stratificationRMSDUnboundLabel, 1, 6)
234
+ self.stratificationLayout.addWidget(self.stratificationRMSDUnboundLineEdit, 1, 7)
235
+
236
+ self.stratification.setLayout(self.stratificationLayout)
237
+
238
+ # compatibility
239
+ self.compatibility = QGroupBox('Compatibility')
240
+ self.compatibilityLayout = QGridLayout()
241
+
242
+ self.pinDownProCheckbox = QCheckBox('Pinning down the protein')
243
+ self.pinDownProCheckbox.setChecked(True)
244
+
245
+ self.useOldCvCheckbox = QCheckBox('Use quaternion-based CVs')
246
+ self.useOldCvCheckbox.setChecked(False)
247
+
248
+ self.reflectingBoundaryCheckbox = QCheckBox('Use reflecting boundary')
249
+ self.reflectingBoundaryCheckbox.setChecked(True)
250
+
251
+ self.useCUDASOAIntegrator = QCheckBox('Use CUDASOA integrator')
252
+ self.useCUDASOAIntegrator.setChecked(False)
253
+
254
+ self.compatibilityLayout.addWidget(self.pinDownProCheckbox, 0, 0)
255
+ self.compatibilityLayout.addWidget(self.useOldCvCheckbox, 0, 1)
256
+ self.compatibilityLayout.addWidget(self.reflectingBoundaryCheckbox, 1, 0)
257
+ self.compatibilityLayout.addWidget(self.useCUDASOAIntegrator, 1, 1)
258
+ self.compatibility.setLayout(self.compatibilityLayout)
259
+
260
+ # force field settings
261
+ self.FFSettings = QGroupBox('Force field settings')
262
+ self.FFSettingsLayout = QHBoxLayout()
263
+
264
+ self.OPLSMixingRuleCheckbox = QCheckBox('OPLS mixing rules')
265
+ self.OPLSMixingRuleCheckbox.setChecked(False)
266
+
267
+ self.timestepLayout = QHBoxLayout()
268
+ self.timestepLabel = QLabel(' Timestep:')
269
+ self.timestepLineEdit = QLineEdit('2.0')
270
+ self.timestepLayout.addWidget(self.timestepLabel)
271
+ self.timestepLayout.addWidget(self.timestepLineEdit)
272
+
273
+ self.FFSettingsLayout.addWidget(self.OPLSMixingRuleCheckbox)
274
+ self.FFSettingsLayout.addLayout(self.timestepLayout)
275
+ self.FFSettings.setLayout(self.FFSettingsLayout)
276
+
277
+ # strategy settings
278
+ self.strategy = QGroupBox('Strategy settings')
279
+ self.strategyLayout = QHBoxLayout()
280
+
281
+ self.considerRMSDCVCheckbox = QCheckBox('Take into account RMSD CV')
282
+ self.considerRMSDCVCheckbox.setChecked(True)
283
+
284
+ self.useGaWTMCheckbox = QCheckBox('Use GaWTM-eABF')
285
+ self.useGaWTMCheckbox.setChecked(False)
286
+
287
+ self.strategyLayout.addWidget(self.considerRMSDCVCheckbox)
288
+ self.strategyLayout.addWidget(self.useGaWTMCheckbox)
289
+ self.strategy.setLayout(self.strategyLayout)
290
+
291
+ # membrane protein
292
+ self.modeling = QGroupBox('Modeling (avaiable for CHARMM FF)')
293
+ self.modelingLayout = QVBoxLayout()
294
+
295
+ self.memProCheckbox = QCheckBox('Membrane protein')
296
+ self.memProCheckbox.setChecked(False)
297
+
298
+ self.neutralizeLigOnlyLayout = QHBoxLayout()
299
+ self.neutralizeLigOnlyLabel = QLabel('Auto-neutralize ligand-only system by:')
300
+ self.neutralizeLigOnlyCombobox = QComboBox()
301
+ self.neutralizeLigOnlyCombobox.addItem('NaCl')
302
+ self.neutralizeLigOnlyCombobox.addItem('KCl')
303
+ self.neutralizeLigOnlyCombobox.addItem('CaCl2')
304
+ self.neutralizeLigOnlyCombobox.addItem('None')
305
+ self.neutralizeLigOnlyLayout.addWidget(self.neutralizeLigOnlyLabel)
306
+ self.neutralizeLigOnlyLayout.addWidget(self.neutralizeLigOnlyCombobox)
307
+
308
+ self.modelingLayout.addWidget(self.memProCheckbox)
309
+ self.modelingLayout.addLayout(self.neutralizeLigOnlyLayout)
310
+ self.modeling.setLayout(self.modelingLayout)
311
+
312
+ # parallel runs for error estimation
313
+ self.parallelRuns = QGroupBox('Parallel runs')
314
+ self.parallelRunsLayout = QHBoxLayout()
315
+
316
+ self.parallelRunsLabel = QLabel('Number of parallel runs: ')
317
+ self.parallelRunsLineEdit = QLineEdit('1')
318
+
319
+ self.parallelRunsLayout.addWidget(self.parallelRunsLabel)
320
+ self.parallelRunsLayout.addWidget(self.parallelRunsLineEdit)
321
+ self.parallelRuns.setLayout(self.parallelRunsLayout)
322
+
323
+
324
+ self.geometricAdvancedSettingsButtonLayout = QHBoxLayout()
325
+ self.geometricAdvancedSettingsOKButton = QPushButton('OK')
326
+ #self.geometricAdvancedSettingsCancelButton = QPushButton('Cancel')
327
+ self.geometricAdvancedSettingsButtonLayout.addWidget(QSplitter())
328
+ self.geometricAdvancedSettingsButtonLayout.addWidget(self.geometricAdvancedSettingsOKButton)
329
+ #self.geometricAdvancedSettingsButtonLayout.addWidget(self.geometricAdvancedSettingsCancelButton)
330
+
331
+ self.mainLayout.addWidget(self.userDefinedDirection)
332
+ self.mainLayout.addWidget(self.nonStandardSolvent)
333
+ self.mainLayout.addWidget(self.stratification)
334
+ self.mainLayout.addWidget(self.compatibility)
335
+ self.mainLayout.addWidget(self.FFSettings)
336
+ self.mainLayout.addWidget(self.strategy)
337
+ self.mainLayout.addWidget(self.modeling)
338
+ self.mainLayout.addWidget(self.parallelRuns)
339
+ self.mainLayout.addLayout(self.geometricAdvancedSettingsButtonLayout)
340
+ self.setLayout(self.mainLayout)
341
+
342
+ def _initSingalsSlots(self):
343
+ """initialize (connect) signial and slots for geometric advanced settings
344
+ """
345
+
346
+ self.nonStandardSolventPsfButton.clicked.connect(
347
+ commonSlots.openFileDialog(
348
+ 'psf/parm/top',
349
+ self.nonStandardSolventPsfLineEdit
350
+ )
351
+ )
352
+ self.nonStandardSolventPdbButton.clicked.connect(
353
+ commonSlots.openFileDialog(
354
+ 'pdb/gro',
355
+ self.nonStandardSolventPdbLineEdit
356
+ )
357
+ )
358
+ self.geometricAdvancedSettingsOKButton.clicked.connect(self.close)
359
+
360
+
361
+ class alchemicalAdvancedSettings(QWidget):
362
+ """advanced settings for the alchemical route
363
+ set the number of stratification windows
364
+ """
365
+
366
+ def __init__(self):
367
+ super().__init__()
368
+ self._initUI()
369
+ self._initSingalsSlots()
370
+ self.setWindowTitle('Alchemical advanced settings')
371
+ self.setGeometry(0,0,0,0)
372
+ #self.show()
373
+
374
+ def _initUI(self):
375
+ """initialize UI for the alchemical advanced settings
376
+ """
377
+
378
+ self.mainLayout = QVBoxLayout()
379
+
380
+ # stratification windows
381
+ self.stratification = QGroupBox('Stratification (number of strata)')
382
+ self.stratificationLayout = QGridLayout()
383
+
384
+ self.boundLigandLabel = QLabel('Ligand/Bound state:')
385
+ self.boundLigandLineEdit = QLineEdit('50')
386
+ self.unboundLigandLabel = QLabel('Ligand/Unbound state:')
387
+ self.unboundLigandLineEdit = QLineEdit('20')
388
+ self.boundRestraintsLabel = QLabel('Restraints/Bound state:')
389
+ self.boundRestraintsLineEdit = QLineEdit('50')
390
+ self.unboundRestraintsLabel = QLabel('Restraints/Unbound state:')
391
+ self.unboundRestraintsLineEdit = QLineEdit('20')
392
+
393
+ self.stratificationLayout.addWidget(self.boundLigandLabel, 0, 0)
394
+ self.stratificationLayout.addWidget(self.boundLigandLineEdit, 0, 1)
395
+ self.stratificationLayout.addWidget(self.unboundLigandLabel, 0, 2)
396
+ self.stratificationLayout.addWidget(self.unboundLigandLineEdit, 0, 3)
397
+ self.stratificationLayout.addWidget(self.boundRestraintsLabel, 1, 0)
398
+ self.stratificationLayout.addWidget(self.boundRestraintsLineEdit, 1, 1)
399
+ self.stratificationLayout.addWidget(self.unboundRestraintsLabel, 1, 2)
400
+ self.stratificationLayout.addWidget(self.unboundRestraintsLineEdit, 1, 3)
401
+
402
+ self.stratification.setLayout(self.stratificationLayout)
403
+
404
+ # double-wide simulation
405
+ self.doubleWide = QGroupBox('Double-wide sampling simulation')
406
+ self.doubleWideLayout = QGridLayout()
407
+
408
+ self.doubleWideCheckbox = QCheckBox('Generate input files for double-wide sampling')
409
+ self.doubleWideCheckbox.setChecked(False)
410
+ self.doubleWideLayout.addWidget(self.doubleWideCheckbox)
411
+ self.doubleWide.setLayout(self.doubleWideLayout)
412
+
413
+ # minimize before sampling in each window
414
+ self.minBeforeSample = QGroupBox('Minimization before sampling')
415
+ self.minBeforeSampleLayout = QVBoxLayout()
416
+
417
+ self.minBeforeSampleCheckbox = QCheckBox('Minimize before sampling in each window')
418
+ self.minBeforeSampleCheckbox.setChecked(False)
419
+ self.minBeforeSampleLayout.addWidget(self.minBeforeSampleCheckbox)
420
+ self.minBeforeSample.setLayout(self.minBeforeSampleLayout)
421
+
422
+ # compatibility
423
+ self.compatibility = QGroupBox('Compatibility')
424
+ self.compatibilityLayout = QGridLayout()
425
+
426
+ self.pinDownProCheckbox = QCheckBox('Pinning down the protein')
427
+ self.pinDownProCheckbox.setChecked(True)
428
+
429
+ self.useOldCvCheckbox = QCheckBox('Use quaternion-based CVs')
430
+ self.useOldCvCheckbox.setChecked(False)
431
+
432
+ self.useCUDASOAIntegrator = QCheckBox('Use CUDASOA integrator')
433
+ self.useCUDASOAIntegrator.setChecked(False)
434
+
435
+ self.compatibilityLayout.addWidget(self.pinDownProCheckbox, 0, 0)
436
+ self.compatibilityLayout.addWidget(self.useOldCvCheckbox, 0, 1)
437
+ self.compatibilityLayout.addWidget(self.useCUDASOAIntegrator, 1, 0)
438
+ self.compatibility.setLayout(self.compatibilityLayout)
439
+
440
+ # force field settings
441
+ self.FFSettings = QGroupBox('Force field settings')
442
+ self.FFSettingsLayout = QHBoxLayout()
443
+
444
+ self.OPLSMixingRuleCheckbox = QCheckBox('OPLS mixing rules')
445
+ self.OPLSMixingRuleCheckbox.setChecked(False)
446
+
447
+ self.timestepLayout = QHBoxLayout()
448
+ self.timestepLabel = QLabel(' Timestep:')
449
+ self.timestepLineEdit = QLineEdit('2.0')
450
+ self.timestepLayout.addWidget(self.timestepLabel)
451
+ self.timestepLayout.addWidget(self.timestepLineEdit)
452
+
453
+ self.FFSettingsLayout.addWidget(self.OPLSMixingRuleCheckbox)
454
+ self.FFSettingsLayout.addLayout(self.timestepLayout)
455
+ self.FFSettings.setLayout(self.FFSettingsLayout)
456
+
457
+ # strategy settings
458
+ self.strategy = QGroupBox('Strategy settings')
459
+ self.strategyLayout = QHBoxLayout()
460
+
461
+ self.considerRMSDCVCheckbox = QCheckBox('Take into account RMSD CV')
462
+ self.considerRMSDCVCheckbox.setChecked(True)
463
+
464
+ self.reEqCheckbox = QCheckBox('Re-equilibration after histogram')
465
+ self.reEqCheckbox.setChecked(False)
466
+
467
+ self.strategyLayout.addWidget(self.considerRMSDCVCheckbox)
468
+ self.strategyLayout.addWidget(self.reEqCheckbox)
469
+ self.strategy.setLayout(self.strategyLayout)
470
+
471
+ # membrane protein
472
+ self.modeling = QGroupBox('Modeling (avaiable for CHARMM FF)')
473
+ self.modelingLayout = QVBoxLayout()
474
+
475
+ self.memProCheckbox = QCheckBox('Membrane protein')
476
+ self.memProCheckbox.setChecked(False)
477
+
478
+ self.neutralizeLigOnlyLayout = QHBoxLayout()
479
+ self.neutralizeLigOnlyLabel = QLabel('Auto-neutralize ligand-only system by:')
480
+ self.neutralizeLigOnlyCombobox = QComboBox()
481
+ self.neutralizeLigOnlyCombobox.addItem('NaCl')
482
+ self.neutralizeLigOnlyCombobox.addItem('KCl')
483
+ self.neutralizeLigOnlyCombobox.addItem('CaCl2')
484
+ self.neutralizeLigOnlyCombobox.addItem('None')
485
+ self.neutralizeLigOnlyLayout.addWidget(self.neutralizeLigOnlyLabel)
486
+ self.neutralizeLigOnlyLayout.addWidget(self.neutralizeLigOnlyCombobox)
487
+
488
+ self.modelingLayout.addWidget(self.memProCheckbox)
489
+ self.modelingLayout.addLayout(self.neutralizeLigOnlyLayout)
490
+ self.modeling.setLayout(self.modelingLayout)
491
+
492
+ self.alchemicalAdvancedSettingsButtonLayout = QHBoxLayout()
493
+ self.alchemicalAdvancedSettingsOKButton = QPushButton('OK')
494
+ #self.alchemicalAdvancedSettingsCancelButton = QPushButton('Cancel')
495
+ self.alchemicalAdvancedSettingsButtonLayout.addWidget(QSplitter())
496
+ self.alchemicalAdvancedSettingsButtonLayout.addWidget(self.alchemicalAdvancedSettingsOKButton)
497
+ #self.alchemicalAdvancedSettingsButtonLayout.addWidget(self.alchemicalAdvancedSettingsCancelButton)
498
+
499
+ self.mainLayout.addWidget(self.stratification)
500
+ self.mainLayout.addWidget(self.doubleWide)
501
+ self.mainLayout.addWidget(self.compatibility)
502
+ self.mainLayout.addWidget(self.FFSettings)
503
+ self.mainLayout.addWidget(self.strategy)
504
+ self.mainLayout.addWidget(self.minBeforeSample)
505
+ self.mainLayout.addWidget(self.modeling)
506
+ self.mainLayout.addLayout(self.alchemicalAdvancedSettingsButtonLayout)
507
+ self.setLayout(self.mainLayout)
508
+
509
+ def _initSingalsSlots(self):
510
+ """initialize (connect) signals and slots for the alchemical advanced settings
511
+ """
512
+
513
+ self.alchemicalAdvancedSettingsOKButton.clicked.connect(self.close)
514
+
515
+ class mainUI(QMainWindow):
516
+ """the main window UI
517
+ include the preTreatment, postTreatment and QuickPlot tab
518
+ the preTreatment tab include NAMD and Gromacs tab
519
+ the postTreatment tab include geometric and alchemical tab
520
+ """
521
+
522
+ def __init__(self):
523
+ super().__init__()
524
+
525
+ self._initActions()
526
+
527
+ self._initNAMDTab()
528
+ self._initGromacsTab()
529
+ self._initPreTreatmentTab()
530
+ self._initQuickPlotTab()
531
+
532
+ self._initGeometricTab()
533
+ self._initAlchemicalTab()
534
+ self._initPostTreatmentTab()
535
+
536
+ self._initSingalsSlots()
537
+
538
+ self._initMainUI()
539
+
540
+ # other dialogs
541
+ self.mainSettings = mainSettings()
542
+ self.alchemicalAdvancedSettings = alchemicalAdvancedSettings()
543
+ self.geometricAdvancedSettings = geometricAdvancedSettings()
544
+
545
+ self.setGeometry(0,0,0,0)
546
+ self.setWindowTitle(__PROGRAM_NAME__)
547
+ self.setWindowIcon(QIcon("BFEE2/icon/icon.png"))
548
+ self.show()
549
+
550
+ self._showNAMDVersionWarning()
551
+
552
+ def _showNAMDVersionWarning(self):
553
+ ''' show a message box ask for the latest NAMD version '''
554
+ QMessageBox.warning(self,
555
+ 'Warning',
556
+ f'\
557
+ {__PROGRAM_NAME__} is fully compatible with NAMD {__NAMD_VERSION__}. \n\
558
+ Please use the same or a later version of NAMD if you have any problem.\n'
559
+ )
560
+
561
+ def _initActions(self):
562
+ ''' initialize actions for menubar '''
563
+
564
+ # settings
565
+ self.settingsAction = QAction('&Settings', self)
566
+ self.settingsAction.setStatusTip('Set pathes for third-party softwares')
567
+ self.settingsAction.triggered.connect(self._mainSettings())
568
+
569
+ # exit
570
+ self.exitAction = QAction('&Exit', self)
571
+ self.exitAction.setStatusTip('Exit application')
572
+ self.exitAction.triggered.connect(QApplication.quit)
573
+
574
+ # help
575
+ self.helpAction = QAction('&Help', self)
576
+ self.helpAction.setStatusTip('Open user manual')
577
+ self.helpAction.triggered.connect(self._openDocFile)
578
+
579
+ # python API
580
+ self.pythonAPIAction = QAction('&Python API', self)
581
+ self.pythonAPIAction.setStatusTip('Open Python API Documentation')
582
+ self.pythonAPIAction.triggered.connect(self._openPythonAPIFile)
583
+
584
+ # about
585
+ self.aboutAction = QAction('&About', self)
586
+ self.aboutAction.setStatusTip('About BFEEstimator')
587
+ self.aboutAction.triggered.connect(self._showAboutBox())
588
+
589
+
590
+ def _initMainUI(self):
591
+ """initialize main window
592
+ """
593
+
594
+ # status bar
595
+ #self.statusBar()
596
+
597
+ # menu bar
598
+ menubar = self.menuBar()
599
+ menubar.setNativeMenuBar(False)
600
+ self.fileMenu = menubar.addMenu('&File')
601
+ self.fileMenu.addAction(self.settingsAction)
602
+ self.fileMenu.addSeparator()
603
+ self.fileMenu.addAction(self.exitAction)
604
+
605
+ self.helpMenu = menubar.addMenu('&Help')
606
+ self.helpMenu.addAction(self.helpAction)
607
+ self.helpMenu.addAction(self.pythonAPIAction)
608
+ self.helpMenu.addSeparator()
609
+ self.helpMenu.addAction(self.aboutAction)
610
+
611
+ # main layout
612
+ self.mainLayout = QVBoxLayout()
613
+
614
+ # title
615
+ self.title = QLabel('Binding Free Energy Estimator')
616
+ titleFont = QFont()
617
+ titleFont.setBold(True)
618
+ self.title.setFont(titleFont)
619
+ self.titleBox = QGroupBox()
620
+ self.titleBoxLayout = QVBoxLayout()
621
+ self.titleBoxLayout.addWidget(self.title, alignment=QtCore.Qt.AlignCenter)
622
+ self.titleBox.setLayout(self.titleBoxLayout)
623
+
624
+ # tabs
625
+ self.mainTabs = QTabWidget()
626
+ #self.preTreatmentTab = QWidget()
627
+ #self.postTreatmentTab = QWidget()
628
+ #self.quickPlot = QWidget()
629
+
630
+ self.mainTabs.addTab(self.preTreatmentTab, 'Pre-treatment')
631
+ self.mainTabs.addTab(self.postTreatmentTab, 'Post-treatment')
632
+ self.mainTabs.addTab(self.quickPlot, 'Quick-Plot')
633
+
634
+ # main layout
635
+ #self.mainLayout.addWidget(self.titleBox)
636
+ self.mainLayout.addWidget(self.mainTabs)
637
+ self.mainWidgit = QWidget()
638
+ self.mainWidgit.setLayout(self.mainLayout)
639
+ self.setCentralWidget(self.mainWidgit)
640
+
641
+ def _initPreTreatmentTab(self):
642
+ """initialize pre-treatment tab
643
+ """
644
+
645
+ self.preTreatmentTab = QWidget()
646
+
647
+ # pre-treatment tabs
648
+ # NAMD and gromacs
649
+ self.preTreatmentMainTabs = QTabWidget()
650
+
651
+ self.preTreatmentMainTabs.addTab(self.NAMDTab, 'NAMD/Gromacs(CHARMM/Amber files)')
652
+ self.preTreatmentMainTabs.addTab(self.GromacsTab, 'Gromacs(Gromacs files)')
653
+
654
+ self.preTreatmentMainLayout = QVBoxLayout()
655
+ self.preTreatmentMainLayout.addWidget(self.preTreatmentMainTabs)
656
+
657
+ # other parameters
658
+ self.otherParameters = QGroupBox('Other parameters')
659
+ self.otherParametersLayout = QVBoxLayout()
660
+
661
+ # temperature, selection protein and ligand layout
662
+ self.otherParametersChildLayout = QGridLayout()
663
+
664
+ # temperature
665
+ self.temperatureLabel = QLabel('Temperature: ')
666
+ self.temperatureLineEdit = QLineEdit('300')
667
+ self.otherParametersChildLayout.addWidget(self.temperatureLabel, 0, 0)
668
+ self.otherParametersChildLayout.addWidget(self.temperatureLineEdit, 0, 1)
669
+
670
+ # select protein
671
+ self.selectProteinLabel = QLabel('Select protein: ')
672
+ self.selectProteineLineEdit = QLineEdit('segid SH3D')
673
+ self.otherParametersChildLayout.addWidget(self.selectProteinLabel, 1, 0)
674
+ self.otherParametersChildLayout.addWidget(self.selectProteineLineEdit, 1, 1)
675
+
676
+ # select ligand
677
+ self.selectLigandLabel = QLabel('Select ligand: ')
678
+ self.selectLigandLineEdit = QLineEdit('segid PPRO')
679
+ self.otherParametersChildLayout.addWidget(self.selectLigandLabel, 2, 0)
680
+ self.otherParametersChildLayout.addWidget(self.selectLigandLineEdit, 2, 1)
681
+
682
+ # select strategy
683
+ self.selectStrategyLayout = QHBoxLayout()
684
+
685
+ self.selectStrategyLabel = QLabel('Select MD engine and strategy: ')
686
+ self.selectStrategyCombobox = QComboBox()
687
+ self.selectStrategyCombobox.addItem('Geometric')
688
+ self.selectStrategyCombobox.addItem('Alchemical')
689
+ self.selectStrategyAdvancedButton = QPushButton('Advanced settings')
690
+
691
+ self.selectMDEngineCombobox = QComboBox()
692
+ self.selectMDEngineCombobox.addItem('NAMD')
693
+ self.selectMDEngineCombobox.addItem('Gromacs')
694
+
695
+ self.selectStrategyChildLayout = QHBoxLayout()
696
+ self.selectStrategyChildLayout.addWidget(self.selectMDEngineCombobox)
697
+ self.selectStrategyChildLayout.addWidget(self.selectStrategyCombobox)
698
+ self.selectStrategyChildLayout.addWidget(self.selectStrategyAdvancedButton)
699
+
700
+ self.selectStrategyLayout.addWidget(self.selectStrategyLabel)
701
+ self.selectStrategyLayout.addLayout(self.selectStrategyChildLayout)
702
+
703
+
704
+ # generate input button
705
+ self.generateInputButton = QPushButton('Generate Inputs')
706
+
707
+ self.otherParametersLayout.addLayout(self.otherParametersChildLayout)
708
+ self.otherParametersLayout.addLayout(self.selectStrategyLayout)
709
+ self.otherParameters.setLayout(self.otherParametersLayout)
710
+
711
+ self.preTreatmentMainLayout.addWidget(self.otherParameters)
712
+ self.preTreatmentMainLayout.addWidget(self.generateInputButton)
713
+
714
+ self.preTreatmentTab.setLayout(self.preTreatmentMainLayout)
715
+
716
+ def _initNAMDTab(self):
717
+ """initialize NAMD Tab in pre-treatment Tab
718
+ """
719
+
720
+ self.NAMDTab = QWidget()
721
+ self.NAMDTabMainLayout = QVBoxLayout()
722
+
723
+ # inputs for the complex
724
+ self.inputsForComplex = QGroupBox('Inputs for complex')
725
+ self.inputsForComplexLayout = QGridLayout()
726
+
727
+ # psf/parm
728
+ self.psfLabel = QLabel('psf/parm file:')
729
+ self.psfLineEdit = QLineEdit()
730
+ self.psfButton = QPushButton('Browse')
731
+ self.inputsForComplexLayout.addWidget(self.psfLabel, 0, 0)
732
+ self.inputsForComplexLayout.addWidget(self.psfLineEdit, 0, 1)
733
+ self.inputsForComplexLayout.addWidget(self.psfButton, 0, 2)
734
+
735
+ # coor
736
+ self.coorLabel = QLabel('pdb/rst file:')
737
+ self.coorLineEdit = QLineEdit()
738
+ self.coorButton = QPushButton('Browse')
739
+ self.inputsForComplexLayout.addWidget(self.coorLabel, 1, 0)
740
+ self.inputsForComplexLayout.addWidget(self.coorLineEdit, 1, 1)
741
+ self.inputsForComplexLayout.addWidget(self.coorButton, 1, 2)
742
+
743
+ # force fields
744
+ self.forceFields = QGroupBox('Force fields')
745
+ self.forceFieldsLayout = QVBoxLayout()
746
+
747
+ # force field type
748
+ self.forceFieldTypeLayout = QHBoxLayout()
749
+ self.forceFieldTypeLabel = QLabel('Force field type:')
750
+ self.forceFieldCombobox = QComboBox()
751
+ self.forceFieldCombobox.addItem('CHARMM')
752
+ self.forceFieldCombobox.addItem('Amber')
753
+
754
+ self.forceFieldTypeLayout.addWidget(self.forceFieldTypeLabel)
755
+ self.forceFieldTypeLayout.addWidget(self.forceFieldCombobox)
756
+
757
+ # CHARMM force field files
758
+ self.forceFieldFilesLayout = QVBoxLayout()
759
+ self.forceFieldFilesLabel = QLabel('Force field files:')
760
+ self.forceFieldFilesBox = QListWidget()
761
+ self.forceFieldFilesChildLayout = QHBoxLayout()
762
+ self.forceFieldAddButton = QPushButton('Add')
763
+ self.forceFieldClearButton = QPushButton('Clear')
764
+ self.forceFieldFilesChildLayout.addWidget(self.forceFieldAddButton)
765
+ self.forceFieldFilesChildLayout.addWidget(self.forceFieldClearButton)
766
+ self.forceFieldFilesLayout.addWidget(self.forceFieldFilesLabel)
767
+ self.forceFieldFilesLayout.addWidget(self.forceFieldFilesBox)
768
+ self.forceFieldFilesLayout.addLayout(self.forceFieldFilesChildLayout)
769
+
770
+ self.forceFieldsLayout.addLayout(self.forceFieldTypeLayout)
771
+ self.forceFieldsLayout.addLayout(self.forceFieldFilesLayout)
772
+
773
+ self.inputsForComplex.setLayout(self.inputsForComplexLayout)
774
+ self.forceFields.setLayout(self.forceFieldsLayout)
775
+ self.NAMDTabMainLayout.addWidget(self.inputsForComplex)
776
+ self.NAMDTabMainLayout.addWidget(self.forceFields)
777
+ self.NAMDTab.setLayout(self.NAMDTabMainLayout)
778
+
779
+ def _initGromacsTab(self):
780
+ """initialize GMX Tab in pre-treatment Tab
781
+ """
782
+
783
+ self.GromacsTab = QWidget()
784
+ self.GromacsTabMainLayout = QVBoxLayout()
785
+
786
+ # inputs for the complex
787
+ self.GromacsInputsForComplex = QGroupBox('Inputs for complex')
788
+ self.GromacsInputsForComplexLayout = QGridLayout()
789
+
790
+ # top
791
+ self.topLabel = QLabel('Topology file: ')
792
+ self.topLineEdit = QLineEdit()
793
+ self.topButton = QPushButton('Browse')
794
+ self.GromacsInputsForComplexLayout.addWidget(self.topLabel, 0, 0)
795
+ self.GromacsInputsForComplexLayout.addWidget(self.topLineEdit, 0, 1)
796
+ self.GromacsInputsForComplexLayout.addWidget(self.topButton, 0, 2)
797
+
798
+ # gro
799
+ self.gromacsPdbLabel = QLabel('Structure file: ')
800
+ self.gromacsPdbLineEdit = QLineEdit()
801
+ self.gromacsPdbButton = QPushButton('Browse')
802
+ self.GromacsInputsForComplexLayout.addWidget(self.gromacsPdbLabel, 1, 0)
803
+ self.GromacsInputsForComplexLayout.addWidget(self.gromacsPdbLineEdit, 1, 1)
804
+ self.GromacsInputsForComplexLayout.addWidget(self.gromacsPdbButton, 1, 2)
805
+
806
+ # structure file format
807
+ self.gromacsStructureFileFormatLayout = QHBoxLayout()
808
+ self.gromacsStructureFileFormatLabel = QLabel('Structure file format:')
809
+ self.gromacsStructureFileFormatCombobox = QComboBox()
810
+ self.gromacsStructureFileFormatCombobox.addItem('pdb')
811
+ self.gromacsStructureFileFormatCombobox.addItem('xpdb')
812
+ self.gromacsStructureFileFormatCombobox.setToolTip('Select "<b>xpdb</b>" if your PDB file has more than 9,999 number of residues')
813
+ self.gromacsStructureFileFormatLayout.addWidget(self.gromacsStructureFileFormatLabel)
814
+ self.gromacsStructureFileFormatLayout.addWidget(self.gromacsStructureFileFormatCombobox)
815
+ self.GromacsInputsForComplexLayout.addLayout(self.gromacsStructureFileFormatLayout, 2, 0, 1, 3)
816
+
817
+ self.GromacsInputsForComplexLayout.addWidget(QSplitter(), 3, 1)
818
+ self.GromacsInputsForComplex.setLayout(self.GromacsInputsForComplexLayout)
819
+
820
+ # inputs for the ligand-only system
821
+ self.GromacsInputsLigandOnly = QGroupBox('Inputs for ligand-only system')
822
+ self.GromacsInputsLigandOnlyLayout = QGridLayout()
823
+
824
+ # top
825
+ self.gromacsLigandOnlyTopLabel = QLabel('Topology file: ')
826
+ self.gromacsLigandOnlyTopLineEdit = QLineEdit()
827
+ self.gromacsLigandOnlyTopButton = QPushButton('Browse')
828
+ self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyTopLabel, 0, 0)
829
+ self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyTopLineEdit, 0, 1)
830
+ self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyTopButton, 0, 2)
831
+
832
+ # gro
833
+ self.gromacsLigandOnlyPdbLabel = QLabel('Structure file: ')
834
+ self.gromacsLigandOnlyPdbLineEdit = QLineEdit()
835
+ self.gromacsLigandOnlyPdbButton = QPushButton('Browse')
836
+ self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyPdbLabel, 1, 0)
837
+ self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyPdbLineEdit, 1, 1)
838
+ self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyPdbButton, 1, 2)
839
+
840
+ # structure file format
841
+ self.gromacsLigandOnlyStructureFileFormatLayout = QHBoxLayout()
842
+ self.gromacsLigandOnlyStructureFileFormatLabel = QLabel('Structure file format:')
843
+ self.gromacsLigandOnlyStructureFileFormatCombobox = QComboBox()
844
+ self.gromacsLigandOnlyStructureFileFormatCombobox.addItem('pdb')
845
+ self.gromacsLigandOnlyStructureFileFormatCombobox.addItem('xpdb')
846
+ self.gromacsLigandOnlyStructureFileFormatCombobox.setToolTip('Select "<b>xpdb</b>" if your PDB file has more than 9,999 number of residues')
847
+ self.gromacsLigandOnlyStructureFileFormatLayout.addWidget(self.gromacsLigandOnlyStructureFileFormatLabel)
848
+ self.gromacsLigandOnlyStructureFileFormatLayout.addWidget(self.gromacsLigandOnlyStructureFileFormatCombobox)
849
+ self.GromacsInputsLigandOnlyLayout.addLayout(self.gromacsLigandOnlyStructureFileFormatLayout, 2, 0, 1, 3)
850
+
851
+ self.GromacsInputsLigandOnlyLayout.addWidget(QSplitter(), 3, 1)
852
+ self.GromacsInputsLigandOnly.setLayout(self.GromacsInputsLigandOnlyLayout)
853
+
854
+ self.GromacsTabMainLayout.addWidget(self.GromacsInputsForComplex)
855
+ self.GromacsTabMainLayout.addWidget(self.GromacsInputsLigandOnly)
856
+
857
+ self.GromacsTab.setLayout(self.GromacsTabMainLayout)
858
+
859
+ def _initPostTreatmentTab(self):
860
+ """initialize pre-treatment tab
861
+ """
862
+
863
+ self.postTreatmentTab = QWidget()
864
+
865
+ # post-treatment tabs
866
+ # Geometric and alchemical
867
+ self.postTreatmentMainTabs = QTabWidget()
868
+
869
+ self.postTreatmentMainTabs.addTab(self.geometricTab, 'Geometric')
870
+ self.postTreatmentMainTabs.addTab(self.alchemicalTab, 'Alchemical')
871
+
872
+ self.postTreatmentMainLayout = QVBoxLayout()
873
+ self.postTreatmentMainLayout.addWidget(self.postTreatmentMainTabs)
874
+
875
+ self.calculateButton = QPushButton('Calculate binding free energy')
876
+ self.postTreatmentMainLayout.addWidget(self.calculateButton)
877
+
878
+ self.postTreatmentTab.setLayout(self.postTreatmentMainLayout)
879
+
880
+ def _initGeometricTab(self):
881
+ """initialize geometric tab of post-treatment
882
+ """
883
+
884
+ self.geometricTab = QWidget()
885
+ self.geometricTabLayout = QVBoxLayout()
886
+
887
+ # pmf inputs
888
+ self.pmfInputs = QGroupBox('PMF inputs (.czar.pmf/.UI.pmf):')
889
+ self.pmfInputsLayout = QVBoxLayout()
890
+
891
+ # bound stats
892
+ self.boundStateLabel = QLabel('Bound state:')
893
+ self.boundStateLayout = QGridLayout()
894
+
895
+ # RMSD
896
+ self.rmsdBoundLabel = QLabel('RMSD: ')
897
+ self.rmsdBoundLineEdit = QLineEdit()
898
+ self.rmsdBoundButton = QPushButton('Browse')
899
+ self.boundStateLayout.addWidget(self.rmsdBoundLabel, 0, 0)
900
+ self.boundStateLayout.addWidget(self.rmsdBoundLineEdit, 0, 1)
901
+ self.boundStateLayout.addWidget(self.rmsdBoundButton, 0, 2)
902
+
903
+ # Theta
904
+ self.ThetaLabel = QLabel('Theta:')
905
+ self.ThetaLineEdit = QLineEdit()
906
+ self.ThetaButton = QPushButton('Browse')
907
+ self.boundStateLayout.addWidget(self.ThetaLabel, 1, 0)
908
+ self.boundStateLayout.addWidget(self.ThetaLineEdit, 1, 1)
909
+ self.boundStateLayout.addWidget(self.ThetaButton, 1, 2)
910
+
911
+ # Phi
912
+ self.PhiLabel = QLabel('Phi: ')
913
+ self.PhiLineEdit = QLineEdit()
914
+ self.PhiButton = QPushButton('Browse')
915
+ self.boundStateLayout.addWidget(self.PhiLabel, 2, 0)
916
+ self.boundStateLayout.addWidget(self.PhiLineEdit, 2, 1)
917
+ self.boundStateLayout.addWidget(self.PhiButton, 2, 2)
918
+
919
+ # Psi
920
+ self.PsiLabel = QLabel('Psi: ')
921
+ self.PsiLineEdit = QLineEdit()
922
+ self.PsiButton = QPushButton('Browse')
923
+ self.boundStateLayout.addWidget(self.PsiLabel, 3, 0)
924
+ self.boundStateLayout.addWidget(self.PsiLineEdit, 3, 1)
925
+ self.boundStateLayout.addWidget(self.PsiButton, 3, 2)
926
+
927
+ # theta
928
+ self.thetaLayout = QHBoxLayout()
929
+ self.thetaLabel = QLabel('theta:')
930
+ self.thetaLineEdit = QLineEdit()
931
+ self.thetaButton = QPushButton('Browse')
932
+ self.boundStateLayout.addWidget(self.thetaLabel, 4, 0)
933
+ self.boundStateLayout.addWidget(self.thetaLineEdit, 4, 1)
934
+ self.boundStateLayout.addWidget(self.thetaButton, 4, 2)
935
+
936
+ # phi
937
+ self.phiLayout = QHBoxLayout()
938
+ self.phiLabel = QLabel('phi: ')
939
+ self.phiLineEdit = QLineEdit()
940
+ self.phiButton = QPushButton('Browse')
941
+ self.boundStateLayout.addWidget(self.phiLabel, 5, 0)
942
+ self.boundStateLayout.addWidget(self.phiLineEdit, 5, 1)
943
+ self.boundStateLayout.addWidget(self.phiButton, 5, 2)
944
+
945
+ # r
946
+ self.rLabel = QLabel('r: ')
947
+ self.rLineEdit = QLineEdit()
948
+ self.rButton = QPushButton('Browse')
949
+ self.boundStateLayout.addWidget(self.rLabel, 6, 0)
950
+ self.boundStateLayout.addWidget(self.rLineEdit, 6, 1)
951
+ self.boundStateLayout.addWidget(self.rButton, 6, 2)
952
+
953
+ self.unboundStateLabel = QLabel('Unbound state:')
954
+
955
+ # RMSD unbound
956
+ self.rmsdUnboundLayout = QHBoxLayout()
957
+ self.rmsdUnboundLabel = QLabel('RMSD: ')
958
+ self.rmsdUnboundLineEdit = QLineEdit()
959
+ self.rmsdUnboundButton = QPushButton('Browse')
960
+ self.rmsdUnboundLayout.addWidget(self.rmsdUnboundLabel)
961
+ self.rmsdUnboundLayout.addWidget(self.rmsdUnboundLineEdit)
962
+ self.rmsdUnboundLayout.addWidget(self.rmsdUnboundButton)
963
+
964
+ self.pmfInputsLayout.addWidget(self.boundStateLabel)
965
+ self.pmfInputsLayout.addLayout(self.boundStateLayout)
966
+ self.pmfInputsLayout.addWidget(self.unboundStateLabel)
967
+ self.pmfInputsLayout.addLayout(self.rmsdUnboundLayout)
968
+
969
+ self.pmfInputs.setLayout(self.pmfInputsLayout)
970
+
971
+ # force constants
972
+ self.forceConstants = QGroupBox('Force constants (in Colvars unit):')
973
+ self.forceConstantsLayout = QGridLayout()
974
+
975
+ self.fcBoundStateLabel = QLabel('Bound state:')
976
+
977
+ # all widgets
978
+ self.fcRMSDLabel = QLabel('RMSD:')
979
+ self.fcRMSDLineEdit = QLineEdit('10')
980
+ self.fcThetaLabel = QLabel('Theta:')
981
+ self.fcThetaLineEdit = QLineEdit('0.1')
982
+ self.fcPhiLabel = QLabel('Phi:')
983
+ self.fcPhiLineEdit = QLineEdit('0.1')
984
+ self.fcPsiLabel = QLabel('Psi:')
985
+ self.fcPsiLineEdit = QLineEdit('0.1')
986
+ self.fcthetaLabel = QLabel('theta:')
987
+ self.fcthetaLineEdit = QLineEdit('0.1')
988
+ self.fcphiLabel = QLabel('phi:')
989
+ self.fcphiLineEdit = QLineEdit('0.1')
990
+
991
+ self.forceConstantsLayout.addWidget(self.fcRMSDLabel, 0, 0)
992
+ self.forceConstantsLayout.addWidget(self.fcRMSDLineEdit, 0, 1)
993
+ self.forceConstantsLayout.addWidget(self.fcThetaLabel, 0, 2)
994
+ self.forceConstantsLayout.addWidget(self.fcThetaLineEdit, 0, 3)
995
+ self.forceConstantsLayout.addWidget(self.fcPhiLabel, 0, 4)
996
+ self.forceConstantsLayout.addWidget(self.fcPhiLineEdit, 0, 5)
997
+ self.forceConstantsLayout.addWidget(self.fcPsiLabel, 1, 0)
998
+ self.forceConstantsLayout.addWidget(self.fcPsiLineEdit, 1, 1)
999
+ self.forceConstantsLayout.addWidget(self.fcthetaLabel, 1, 2)
1000
+ self.forceConstantsLayout.addWidget(self.fcthetaLineEdit, 1, 3)
1001
+ self.forceConstantsLayout.addWidget(self.fcphiLabel, 1, 4)
1002
+ self.forceConstantsLayout.addWidget(self.fcphiLineEdit, 1, 5)
1003
+
1004
+ self.forceConstants.setLayout(self.forceConstantsLayout)
1005
+
1006
+ # other parameters
1007
+ self.postOtherParams = QGroupBox('Other parameters:')
1008
+ self.postOtherParamsLayout = QHBoxLayout()
1009
+
1010
+ self.postTemperatureLabel = QLabel('temperature:')
1011
+ self.postTemperatureLineEdit = QLineEdit('300')
1012
+ self.postRstarLabel = QLabel(' r*:')
1013
+ self.postRstarLineEdit = QLineEdit('30')
1014
+ self.postPMFTypeLabel = QLabel(' PMF type:')
1015
+ self.postPMFTypeBox = QComboBox()
1016
+ self.postPMFTypeBox.addItem('NAMD')
1017
+ self.postPMFTypeBox.addItem('Gromacs')
1018
+
1019
+ self.postOtherParamsLayout.addWidget(self.postTemperatureLabel)
1020
+ self.postOtherParamsLayout.addWidget(self.postTemperatureLineEdit)
1021
+ self.postOtherParamsLayout.addWidget(self.postRstarLabel)
1022
+ self.postOtherParamsLayout.addWidget(self.postRstarLineEdit)
1023
+ self.postOtherParamsLayout.addWidget(self.postPMFTypeLabel)
1024
+ self.postOtherParamsLayout.addWidget(self.postPMFTypeBox)
1025
+
1026
+ self.postOtherParams.setLayout(self.postOtherParamsLayout)
1027
+
1028
+ self.fcBoundStateLabel = QLabel('Bound state:')
1029
+
1030
+ self.geometricTabLayout.addWidget(self.pmfInputs)
1031
+ self.geometricTabLayout.addWidget(self.forceConstants)
1032
+ self.geometricTabLayout.addWidget(self.postOtherParams)
1033
+ self.geometricTab.setLayout(self.geometricTabLayout)
1034
+
1035
+ def _initAlchemicalTab(self):
1036
+ """initialize alchemical tab of post-treatment
1037
+ """
1038
+
1039
+ self.alchemicalTab = QWidget()
1040
+ self.alchemicalTabLayout = QVBoxLayout()
1041
+
1042
+
1043
+ self.restraintInputs = QGroupBox('Inputs for alchemical simulations (.fepout/.log):')
1044
+ self.restraintInputsLayout = QVBoxLayout()
1045
+
1046
+ # bound state
1047
+ self.alchemicalBoundStateLabel = QLabel('Atoms/Bound state (.fepout):')
1048
+ self.alchemicalBoundStateLayout = QGridLayout()
1049
+
1050
+ self.alchemicalForwardLabel1 = QLabel('Forward:')
1051
+ self.alchemicalForwardLineEdit1 = QLineEdit()
1052
+ self.alchemicalForwardButton1 = QPushButton('Browse')
1053
+ self.alchemicalBoundStateLayout.addWidget(self.alchemicalForwardLabel1, 0, 0)
1054
+ self.alchemicalBoundStateLayout.addWidget(self.alchemicalForwardLineEdit1, 0, 1)
1055
+ self.alchemicalBoundStateLayout.addWidget(self.alchemicalForwardButton1, 0, 2)
1056
+
1057
+ self.alchemicalBackwardLabel1 = QLabel('Backward:')
1058
+ self.alchemicalBackwardLineEdit1 = QLineEdit()
1059
+ self.alchemicalBackwardButton1 = QPushButton('Browse')
1060
+ self.alchemicalBoundStateLayout.addWidget(self.alchemicalBackwardLabel1, 1, 0)
1061
+ self.alchemicalBoundStateLayout.addWidget(self.alchemicalBackwardLineEdit1, 1, 1)
1062
+ self.alchemicalBoundStateLayout.addWidget(self.alchemicalBackwardButton1, 1, 2)
1063
+
1064
+ self.alchemicalBoundStateLabel2 = QLabel('Restraints/Bound state (.log):')
1065
+ self.alchemicalBoundStateLayout2 = QGridLayout()
1066
+
1067
+ self.alchemicalForwardLabel2 = QLabel('Forward:')
1068
+ self.alchemicalForwardLineEdit2 = QLineEdit()
1069
+ self.alchemicalForwardButton2 = QPushButton('Browse')
1070
+ self.alchemicalBoundStateLayout2.addWidget(self.alchemicalForwardLabel2, 0, 0)
1071
+ self.alchemicalBoundStateLayout2.addWidget(self.alchemicalForwardLineEdit2, 0, 1)
1072
+ self.alchemicalBoundStateLayout2.addWidget(self.alchemicalForwardButton2, 0, 2)
1073
+
1074
+ self.alchemicalBackwardLabel2 = QLabel('Backward:')
1075
+ self.alchemicalBackwardLineEdit2 = QLineEdit()
1076
+ self.alchemicalBackwardButton2 = QPushButton('Browse')
1077
+ self.alchemicalBoundStateLayout2.addWidget(self.alchemicalBackwardLabel2, 1, 0)
1078
+ self.alchemicalBoundStateLayout2.addWidget(self.alchemicalBackwardLineEdit2, 1, 1)
1079
+ self.alchemicalBoundStateLayout2.addWidget(self.alchemicalBackwardButton2, 1, 2)
1080
+
1081
+ # unbound state
1082
+
1083
+ self.alchemicalUnboundStateLabel = QLabel('Atoms/Unbound state (.fepout):')
1084
+ self.alchemicalUnboundStateLayout = QGridLayout()
1085
+
1086
+ self.alchemicalForwardLabel3 = QLabel('Forward:')
1087
+ self.alchemicalForwardLineEdit3 = QLineEdit()
1088
+ self.alchemicalForwardButton3 = QPushButton('Browse')
1089
+ self.alchemicalUnboundStateLayout.addWidget(self.alchemicalForwardLabel3, 0, 0)
1090
+ self.alchemicalUnboundStateLayout.addWidget(self.alchemicalForwardLineEdit3, 0, 1)
1091
+ self.alchemicalUnboundStateLayout.addWidget(self.alchemicalForwardButton3, 0, 2)
1092
+
1093
+ self.alchemicalBackwardLabel3 = QLabel('Backward:')
1094
+ self.alchemicalBackwardLineEdit3 = QLineEdit()
1095
+ self.alchemicalBackwardButton3 = QPushButton('Browse')
1096
+ self.alchemicalUnboundStateLayout.addWidget(self.alchemicalBackwardLabel3, 1, 0)
1097
+ self.alchemicalUnboundStateLayout.addWidget(self.alchemicalBackwardLineEdit3, 1, 1)
1098
+ self.alchemicalUnboundStateLayout.addWidget(self.alchemicalBackwardButton3, 1, 2)
1099
+
1100
+ self.alchemicalUnboundStateLabel2 = QLabel('Restraints/Unbound state (.log):')
1101
+ self.alchemicalUnboundStateLayout2 = QGridLayout()
1102
+
1103
+ self.alchemicalForwardLabel4 = QLabel('Forward:')
1104
+ self.alchemicalForwardLineEdit4 = QLineEdit()
1105
+ self.alchemicalForwardButton4 = QPushButton('Browse')
1106
+ self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalForwardLabel4, 0, 0)
1107
+ self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalForwardLineEdit4, 0, 1)
1108
+ self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalForwardButton4, 0, 2)
1109
+
1110
+ self.alchemicalBackwardLabel4 = QLabel('Backward:')
1111
+ self.alchemicalBackwardLineEdit4 = QLineEdit()
1112
+ self.alchemicalBackwardButton4 = QPushButton('Browse')
1113
+ self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalBackwardLabel4, 1, 0)
1114
+ self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalBackwardLineEdit4, 1, 1)
1115
+ self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalBackwardButton4, 1, 2)
1116
+
1117
+ self.restraintInputsLayout.addWidget(self.alchemicalBoundStateLabel)
1118
+ self.restraintInputsLayout.addLayout(self.alchemicalBoundStateLayout)
1119
+ self.restraintInputsLayout.addWidget(self.alchemicalBoundStateLabel2)
1120
+ self.restraintInputsLayout.addLayout(self.alchemicalBoundStateLayout2)
1121
+ self.restraintInputsLayout.addWidget(self.alchemicalUnboundStateLabel)
1122
+ self.restraintInputsLayout.addLayout(self.alchemicalUnboundStateLayout)
1123
+ self.restraintInputsLayout.addWidget(self.alchemicalUnboundStateLabel2)
1124
+ self.restraintInputsLayout.addLayout(self.alchemicalUnboundStateLayout2)
1125
+ self.restraintInputs.setLayout(self.restraintInputsLayout)
1126
+
1127
+ # alchemical force constants
1128
+ self.alchemicalForceConstants = QGroupBox('Force constants (in Colvars unit):')
1129
+
1130
+ # all widgets
1131
+ self.alchemicalFCLayout = QHBoxLayout()
1132
+ self.alchemicalfcThetaLabel = QLabel('Theta:')
1133
+ self.alchemicalfcThetaLineEdit = QLineEdit('0.1')
1134
+ self.alchemicalfcPhiLabel = QLabel(' Phi: ')
1135
+ self.alchemicalfcPhiLineEdit = QLineEdit('0.1')
1136
+ self.alchemicalfcPsiLabel = QLabel('Psi: ')
1137
+ self.alchemicalfcPsiLineEdit = QLineEdit('0.1')
1138
+ self.alchemicalfcthetaLabel = QLabel('theta:')
1139
+ self.alchemicalfcthetaLineEdit = QLineEdit('0.1')
1140
+ self.alchemicalfcphiLabel = QLabel(' phi: ')
1141
+ self.alchemicalfcphiLineEdit = QLineEdit('0.1')
1142
+ self.alchemicalfcRLabel = QLabel('r: ')
1143
+ self.alchemicalfcRLineEdit = QLineEdit('10')
1144
+
1145
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcThetaLabel)
1146
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcThetaLineEdit)
1147
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcPhiLabel)
1148
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcPhiLineEdit)
1149
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcPsiLabel)
1150
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcPsiLineEdit)
1151
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcthetaLabel)
1152
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcthetaLineEdit)
1153
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcphiLabel)
1154
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcphiLineEdit)
1155
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcRLabel)
1156
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcRLineEdit)
1157
+
1158
+ self.alchemicalForceConstants.setLayout(self.alchemicalFCLayout)
1159
+
1160
+ # alchemical restraint centers
1161
+ self.alchemicalRestraintCenters = QGroupBox('Restraint centers (in Colvars unit), temperature and others:')
1162
+
1163
+ # all widgets
1164
+ self.alchemicalRCLayout = QHBoxLayout()
1165
+ self.alchemicalRCThetaLabel = QLabel('Theta:')
1166
+ self.alchemicalRCThetaLineEdit = QLineEdit('0')
1167
+ self.alchemicalRCthetaLabel = QLabel('theta:')
1168
+ self.alchemicalRCthetaLineEdit = QLineEdit('90')
1169
+ self.alchemicalRCRLabel = QLabel('r: ')
1170
+ self.alchemicalRCRLineEdit = QLineEdit('8')
1171
+ self.alchemicalPostTemperatureLabel = QLabel('temperature:')
1172
+ self.alchemicalPostTemperatureLineEdit = QLineEdit('300')
1173
+ self.alchemicalPostTypeLabel = QLabel('Post-treatment type:')
1174
+ self.alchemicalPostTypeBox = QComboBox()
1175
+ self.alchemicalPostTypeBox.addItem('FEP')
1176
+ self.alchemicalPostTypeBox.addItem('BAR')
1177
+
1178
+ self.alchemicalRCLayout.addWidget(self.alchemicalRCThetaLabel)
1179
+ self.alchemicalRCLayout.addWidget(self.alchemicalRCThetaLineEdit)
1180
+ self.alchemicalRCLayout.addWidget(self.alchemicalRCthetaLabel)
1181
+ self.alchemicalRCLayout.addWidget(self.alchemicalRCthetaLineEdit)
1182
+ self.alchemicalRCLayout.addWidget(self.alchemicalRCRLabel)
1183
+ self.alchemicalRCLayout.addWidget(self.alchemicalRCRLineEdit)
1184
+ self.alchemicalRCLayout.addWidget(self.alchemicalPostTemperatureLabel)
1185
+ self.alchemicalRCLayout.addWidget(self.alchemicalPostTemperatureLineEdit)
1186
+ self.alchemicalRCLayout.addWidget(self.alchemicalPostTypeLabel)
1187
+ self.alchemicalRCLayout.addWidget(self.alchemicalPostTypeBox)
1188
+
1189
+ self.alchemicalRestraintCenters.setLayout(self.alchemicalRCLayout)
1190
+
1191
+ self.alchemicalTabLayout.addWidget(self.restraintInputs)
1192
+ self.alchemicalTabLayout.addWidget(self.alchemicalForceConstants)
1193
+ self.alchemicalTabLayout.addWidget(self.alchemicalRestraintCenters)
1194
+ self.alchemicalTab.setLayout(self.alchemicalTabLayout)
1195
+
1196
+ def _initQuickPlotTab(self):
1197
+ """initialize quick-plot tab
1198
+ """
1199
+
1200
+ self.quickPlot = QWidget()
1201
+ self.quickPlotLayout = QVBoxLayout()
1202
+
1203
+ # plot a (stratified) pmf
1204
+ self.plotPmf = QGroupBox('Plot (stratified) PMFs:')
1205
+ self.plotPmfLayout = QVBoxLayout()
1206
+
1207
+ self.plotPmfLabel = QLabel('PMF files:')
1208
+ self.plotPmfBox = QListWidget()
1209
+ self.plotPmfChildLayout = QHBoxLayout()
1210
+ self.plotPmfAddButton = QPushButton('Add')
1211
+ self.plotPmfClearButton = QPushButton('Clear')
1212
+ self.plotPmfPlotButton = QPushButton('Plot')
1213
+ self.plotPmfChildLayout.addWidget(self.plotPmfAddButton)
1214
+ self.plotPmfChildLayout.addWidget(self.plotPmfClearButton)
1215
+ self.plotPmfChildLayout.addWidget(self.plotPmfPlotButton)
1216
+
1217
+ self.plotPmfLayout.addWidget(self.plotPmfLabel)
1218
+ self.plotPmfLayout.addWidget(self.plotPmfBox)
1219
+ self.plotPmfLayout.addLayout(self.plotPmfChildLayout)
1220
+ self.plotPmf.setLayout(self.plotPmfLayout)
1221
+
1222
+ # calculate pmf RMSD convergence
1223
+ self.plotPmfConvergence = QGroupBox('Calculate PMF RMSD convergence:')
1224
+ self.plotPmfConvergenceLayout = QVBoxLayout()
1225
+
1226
+ self.plotPmfConvergenceLabel = QLabel('history file:')
1227
+ self.plotPmfConvergenceBox = QLineEdit()
1228
+ self.plotPmfConvergenceChildLayout = QHBoxLayout()
1229
+ self.plotPmfConvergenceBrowseButton = QPushButton('Browse')
1230
+ self.plotPmfConvergencePlotButton = QPushButton('Plot')
1231
+ self.plotPmfConvergenceChildLayout.addWidget(self.plotPmfConvergenceBrowseButton)
1232
+ self.plotPmfConvergenceChildLayout.addWidget(self.plotPmfConvergencePlotButton)
1233
+
1234
+ self.plotPmfConvergenceLayout.addWidget(self.plotPmfConvergenceLabel)
1235
+ self.plotPmfConvergenceLayout.addWidget(self.plotPmfConvergenceBox)
1236
+ self.plotPmfConvergenceLayout.addLayout(self.plotPmfConvergenceChildLayout)
1237
+ self.plotPmfConvergence.setLayout(self.plotPmfConvergenceLayout)
1238
+
1239
+ # merge a (stratified) pmf
1240
+ self.mergePmf = QGroupBox('Merge (stratified) PMFs:')
1241
+ self.mergePmfLayout = QVBoxLayout()
1242
+
1243
+ self.mergePmfLabel = QLabel('PMF files:')
1244
+ self.mergePmfBox = QListWidget()
1245
+ self.mergePmfChildLayout = QHBoxLayout()
1246
+ self.mergePmfAddButton = QPushButton('Add')
1247
+ self.mergePmfClearButton = QPushButton('Clear')
1248
+ self.mergePmfmergeButton = QPushButton('Merge')
1249
+ self.mergePmfChildLayout.addWidget(self.mergePmfAddButton)
1250
+ self.mergePmfChildLayout.addWidget(self.mergePmfClearButton)
1251
+ self.mergePmfChildLayout.addWidget(self.mergePmfmergeButton)
1252
+
1253
+ self.mergePmfLayout.addWidget(self.mergePmfLabel)
1254
+ self.mergePmfLayout.addWidget(self.mergePmfBox)
1255
+ self.mergePmfLayout.addLayout(self.mergePmfChildLayout)
1256
+ self.mergePmf.setLayout(self.mergePmfLayout)
1257
+
1258
+ # plot hysteresis between forward and backward simulations
1259
+ self.plotHysteresis = QGroupBox('Plot hysteresis between bidirectional simulations:')
1260
+ self.plotHysteresisLayout = QVBoxLayout()
1261
+
1262
+ self.plotHysteresisForwardLayout = QHBoxLayout()
1263
+ self.plotHysteresisForwardLabel = QLabel('Forward (fepout/log): ')
1264
+ self.plotHysteresisForwardLineEdit = QLineEdit()
1265
+ self.plotHysteresisForwardButton = QPushButton('Browse')
1266
+ self.plotHysteresisForwardLayout.addWidget(self.plotHysteresisForwardLabel)
1267
+ self.plotHysteresisForwardLayout.addWidget(self.plotHysteresisForwardLineEdit)
1268
+ self.plotHysteresisForwardLayout.addWidget(self.plotHysteresisForwardButton)
1269
+
1270
+ self.plotHysteresisBackwardLayout = QHBoxLayout()
1271
+ self.plotHysteresisBackwardLabel = QLabel('Backward (fepout/log):')
1272
+ self.plotHysteresisBackwardLineEdit = QLineEdit()
1273
+ self.plotHysteresisBackwardButton = QPushButton('Browse')
1274
+ self.plotHysteresisBackwardLayout.addWidget(self.plotHysteresisBackwardLabel)
1275
+ self.plotHysteresisBackwardLayout.addWidget(self.plotHysteresisBackwardLineEdit)
1276
+ self.plotHysteresisBackwardLayout.addWidget(self.plotHysteresisBackwardButton)
1277
+
1278
+ self.plotHysteresisPlotButton = QPushButton('Plot')
1279
+
1280
+ self.plotHysteresisLayout.addLayout(self.plotHysteresisForwardLayout)
1281
+ self.plotHysteresisLayout.addLayout(self.plotHysteresisBackwardLayout)
1282
+ self.plotHysteresisLayout.addWidget(self.plotHysteresisPlotButton)
1283
+
1284
+ self.plotHysteresis.setLayout(self.plotHysteresisLayout)
1285
+
1286
+ self.quickPlotLayout.addWidget(self.plotPmf)
1287
+ self.quickPlotLayout.addWidget(self.mergePmf)
1288
+ self.quickPlotLayout.addWidget(self.plotPmfConvergence)
1289
+ self.quickPlotLayout.addWidget(self.plotHysteresis)
1290
+ self.quickPlot.setLayout(self.quickPlotLayout)
1291
+
1292
+ # slots are defined below
1293
+ # otherwise they are defined in slots.py
1294
+ def _mainSettings(self):
1295
+ """call main settings
1296
+ """
1297
+ def f():
1298
+ self.mainSettings.show()
1299
+ return f
1300
+
1301
+ def _advancedSettings(self, comboBox):
1302
+ """call advanced settings in pre-treatment
1303
+ the returned function is depended on the comboBox(strategy type)
1304
+ """
1305
+
1306
+ def f():
1307
+ if comboBox.currentText() == 'Geometric':
1308
+ self.geometricAdvancedSettings.show()
1309
+ elif comboBox.currentText() == 'Alchemical':
1310
+ self.alchemicalAdvancedSettings.show()
1311
+
1312
+ return f
1313
+
1314
+ def _changeFFButtonState(self):
1315
+ """enable/disable the add and clear button of force field section
1316
+ """
1317
+
1318
+ if self.forceFieldCombobox.currentText() == 'CHARMM':
1319
+ self.forceFieldAddButton.setEnabled(True)
1320
+ self.forceFieldClearButton.setEnabled(True)
1321
+ self.forceFieldFilesBox.setEnabled(True)
1322
+ elif self.forceFieldCombobox.currentText() == 'Amber':
1323
+ self.forceFieldAddButton.setEnabled(False)
1324
+ self.forceFieldClearButton.setEnabled(False)
1325
+ self.forceFieldFilesBox.setEnabled(False)
1326
+
1327
+ def _changeStrategySettingState(self):
1328
+ """enable/disable the alchemical route and some advanced settings based on the MD engine
1329
+ """
1330
+
1331
+ if self.selectMDEngineCombobox.currentText() == 'NAMD':
1332
+ self.selectStrategyCombobox.setEnabled(True)
1333
+ self.geometricAdvancedSettings.useOldCvCheckbox.setEnabled(True)
1334
+ self.geometricAdvancedSettings.OPLSMixingRuleCheckbox.setEnabled(True)
1335
+ self.geometricAdvancedSettings.useGaWTMCheckbox.setEnabled(True)
1336
+
1337
+ elif self.selectMDEngineCombobox.currentText() == 'Gromacs':
1338
+ index = self.selectStrategyCombobox.findText('Geometric', QtCore.Qt.MatchFixedString)
1339
+ if index >= 0:
1340
+ self.selectStrategyCombobox.setCurrentIndex(index)
1341
+ self.selectStrategyCombobox.setEnabled(False)
1342
+
1343
+ self.geometricAdvancedSettings.useOldCvCheckbox.setChecked(False)
1344
+ self.geometricAdvancedSettings.useOldCvCheckbox.setEnabled(False)
1345
+
1346
+ self.geometricAdvancedSettings.OPLSMixingRuleCheckbox.setChecked(False)
1347
+ self.geometricAdvancedSettings.OPLSMixingRuleCheckbox.setEnabled(False)
1348
+
1349
+ self.geometricAdvancedSettings.useGaWTMCheckbox.setEnabled(False)
1350
+ self.geometricAdvancedSettings.useGaWTMCheckbox.setChecked(False)
1351
+
1352
+ def _changeStrategySettingStateForOldGromacs(self):
1353
+ """enable/disable a lot of options for the old Gromacs tab
1354
+ """
1355
+
1356
+ if self.preTreatmentMainTabs.currentWidget() == self.NAMDTab:
1357
+ self.selectStrategyAdvancedButton.setEnabled(True)
1358
+ self.selectMDEngineCombobox.setEnabled(True)
1359
+
1360
+ elif self.preTreatmentMainTabs.currentWidget() == self.GromacsTab:
1361
+ index = self.selectMDEngineCombobox.findText('Gromacs', QtCore.Qt.MatchFixedString)
1362
+ if index >= 0:
1363
+ self.selectMDEngineCombobox.setCurrentIndex(index)
1364
+
1365
+ index = self.selectStrategyCombobox.findText('Geometric', QtCore.Qt.MatchFixedString)
1366
+ if index >= 0:
1367
+ self.selectStrategyCombobox.setCurrentIndex(index)
1368
+
1369
+ self.selectMDEngineCombobox.setEnabled(False)
1370
+ self.selectStrategyCombobox.setEnabled(False)
1371
+ self.selectStrategyAdvancedButton.setEnabled(False)
1372
+
1373
+ def _openDocFile(self):
1374
+ """open Documentation file
1375
+ """
1376
+
1377
+ webbrowser.open('https://www.nature.com/articles/s41596-021-00676-1')
1378
+
1379
+ def _openPythonAPIFile(self):
1380
+ """open Python API Documentation file
1381
+ """
1382
+
1383
+ webbrowser.open('https://fhh2626.github.io/BFEE2APIDocs/')
1384
+
1385
+ def _showAboutBox(self):
1386
+ """the about message box
1387
+
1388
+ Returns:
1389
+ function obj: a slot function that shows that about message box
1390
+ """
1391
+
1392
+ def f():
1393
+ QMessageBox.about(
1394
+ self,
1395
+ 'About',
1396
+ f'<center><b>{__PROGRAM_NAME__}</b></center><br>'+r'''
1397
+ Binding free energy estimator (BFEE) is a python-based software
1398
+ that automates absolute binding free energy calculations through
1399
+ either the alchemical or geometric route by molecular dynamics
1400
+ simulations.<br>
1401
+ <b>Authors:</b><br>
1402
+ Haohao Fu (<a href="mailto:fhh2626@gmail.com">fhh2626@gmail.com</a>)<br>
1403
+ Haochuan Chen (<a href="mailto:summersnow9403@gmail.com">summersnow9403@gmail.com</a>)<br>
1404
+ <b>License:</b><br>
1405
+ BFEE2 is free software: you can redistribute it and/or modify it
1406
+ under the terms of the GNU General Public License as published by
1407
+ the Free Software Foundation, either version 3 of the License, or
1408
+ (at your option) any later version.<br>
1409
+ <b>Reference:</b><br>
1410
+ <b>BFEE2:</b> Fu et al. Nat. Protoc. 2022, 17, 1114-1141 and Fu et al. J. Chem. Inf. Model. 2021, 61, 2116–2123<br>
1411
+ <b>Alchemical and geometric routes:</b> Gumbart et al. J. Chem. Theory Comput. 2013, 9, 794–802<br>
1412
+ <b>WTM-eABF:</b> Fu et al. Acc. Chem. Res. 2019, 52, 3254–3264 and Fu et al. J. Phys. Chem. Lett. 2018, 9, 4738–4745<br>
1413
+ <b>Collective variables:</b> Fu et al. J. Chem. Theory Comput. 2017, 13, 5173–5178<br>
1414
+ <b>Streamlined geometric route:</b> Fu et al. J. Chem. Inf. Model. 2023, 63, 2512–2519<br>
1415
+ ''')
1416
+ return f
1417
+
1418
+ def _showGeometricResults(self, unit):
1419
+ ''' calculate binding from the geometric route,
1420
+ parameters in the Geometric tab will be read.
1421
+ Show a QMessageBox for the result
1422
+ Inputs:
1423
+ unit (string): 'namd' or 'gromacs' '''
1424
+
1425
+ pTreat = postTreatment.postTreatment(
1426
+ float(self.postTemperatureLineEdit.text()), unit, 'geometric')
1427
+
1428
+ pmfs = [
1429
+ self.rmsdBoundLineEdit.text(),
1430
+ self.ThetaLineEdit.text(),
1431
+ self.PhiLineEdit.text(),
1432
+ self.PsiLineEdit.text(),
1433
+ self.thetaLineEdit.text(),
1434
+ self.phiLineEdit.text(),
1435
+ self.rLineEdit.text(),
1436
+ self.rmsdUnboundLineEdit.text()
1437
+ ]
1438
+
1439
+ try:
1440
+ parameters = [
1441
+ float(self.fcRMSDLineEdit.text()),
1442
+ float(self.fcThetaLineEdit.text()),
1443
+ float(self.fcPhiLineEdit.text()),
1444
+ float(self.fcPsiLineEdit.text()),
1445
+ float(self.fcthetaLineEdit.text()),
1446
+ float(self.fcphiLineEdit.text()),
1447
+ float(self.postRstarLineEdit.text()),
1448
+ float(self.fcRMSDLineEdit.text())
1449
+ ]
1450
+ except:
1451
+ QMessageBox.warning(self, 'Error', f'Force constant or r* input error!')
1452
+ return
1453
+
1454
+ # check inputs
1455
+ for index, item in enumerate(pmfs):
1456
+ if (index != 0) and (index != 7) and (not os.path.exists(item)):
1457
+ QMessageBox.warning(self, 'Error', f'file {item} does not exist!')
1458
+ return
1459
+ if (index == 7) and (not os.path.exists(item)):
1460
+ QMessageBox.warning(self, 'Warning', f'Supposing dealing with a rigid ligand!')
1461
+
1462
+ # calculate free energies
1463
+ try:
1464
+ result = pTreat.geometricBindingFreeEnergy(pmfs, parameters)
1465
+ except postTreatment.RStarTooLargeError:
1466
+ QMessageBox.warning(
1467
+ self,
1468
+ 'Error',
1469
+ f'\
1470
+ r_star cannot be larger than r_max of step 7!\n'
1471
+ )
1472
+ return
1473
+ except Exception as e:
1474
+ print(e)
1475
+ QMessageBox.warning(
1476
+ self,
1477
+ 'Error',
1478
+ f'\
1479
+ Unknown error! The error message is: \n\
1480
+ {e}\n'
1481
+ )
1482
+ return
1483
+
1484
+ QMessageBox.about(
1485
+ self,
1486
+ 'Result',
1487
+ f'\
1488
+ Results:\n\
1489
+ ΔG(site,c) = {result[0]:.2f} kcal/mol\n\
1490
+ ΔG(site,eulerTheta) = {result[1]:.2f} kcal/mol\n\
1491
+ ΔG(site,eulerPhi) = {result[2]:.2f} kcal/mol\n\
1492
+ ΔG(site,eulerPsi) = {result[3]:.2f} kcal/mol\n\
1493
+ ΔG(site,polarTheta) = {result[4]:.2f} kcal/mol\n\
1494
+ ΔG(site,polarPhi) = {result[5]:.2f} kcal/mol\n\
1495
+ (1/beta)*ln(S*I*C0) = {result[6]:.2f} kcal/mol\n\
1496
+ ΔG(bulk,c) = {result[7]:.2f} kcal/mol\n\
1497
+ ΔG(bulk,o) = {result[8]:.2f} kcal/mol\n\
1498
+ \n\
1499
+ Standard Binding Free Energy:\n\
1500
+ ΔG(total) = {result[9]:.2f} kcal/mol\n'
1501
+ )
1502
+
1503
+ def _showAlchemicalResults(self, unit):
1504
+ """calculate binding from the alchemical route,
1505
+ parameters in the alchemical tab will be read.
1506
+ Show a QMessageBox for the result
1507
+
1508
+ Args:
1509
+ unit (str): 'namd' or 'gromacs'
1510
+ """
1511
+
1512
+ pTreat = postTreatment.postTreatment(
1513
+ float(self.alchemicalPostTemperatureLineEdit.text()), unit, 'alchemical')
1514
+
1515
+ # alchemical outputs
1516
+ files = [
1517
+ self.alchemicalForwardLineEdit1.text(),
1518
+ self.alchemicalBackwardLineEdit1.text(),
1519
+ self.alchemicalForwardLineEdit2.text(),
1520
+ self.alchemicalBackwardLineEdit2.text(),
1521
+ self.alchemicalForwardLineEdit3.text(),
1522
+ self.alchemicalBackwardLineEdit3.text(),
1523
+ self.alchemicalForwardLineEdit4.text(),
1524
+ self.alchemicalBackwardLineEdit4.text()
1525
+ ]
1526
+
1527
+ try:
1528
+ parameters = [
1529
+ float(self.alchemicalRCThetaLineEdit.text()),
1530
+ float(self.alchemicalRCthetaLineEdit.text()),
1531
+ float(self.alchemicalRCRLineEdit.text()),
1532
+ float(self.alchemicalfcThetaLineEdit.text()),
1533
+ float(self.alchemicalfcPhiLineEdit.text()),
1534
+ float(self.alchemicalfcPsiLineEdit.text()),
1535
+ float(self.alchemicalfcthetaLineEdit.text()),
1536
+ float(self.alchemicalfcphiLineEdit.text()),
1537
+ float(self.alchemicalfcRLineEdit.text())
1538
+ ]
1539
+ temperature = float(self.alchemicalPostTemperatureLineEdit.text())
1540
+ except:
1541
+ QMessageBox.warning(self, 'Error', f'Force constant or restraint center or temperature input error!')
1542
+ return
1543
+ if self.alchemicalPostTypeBox.currentText() == 'FEP':
1544
+ jobType = 'fep'
1545
+ elif self.alchemicalPostTypeBox.currentText() == 'BAR':
1546
+ jobType = 'bar'
1547
+
1548
+ # check inputs
1549
+ rigid_ligand = False
1550
+ for index, item in enumerate([
1551
+ self.alchemicalBackwardLineEdit1.text(),
1552
+ self.alchemicalBackwardLineEdit2.text(),
1553
+ self.alchemicalBackwardLineEdit3.text(),
1554
+ self.alchemicalBackwardLineEdit4.text()
1555
+ ]):
1556
+ if (not os.path.exists(item)) and (index != 3):
1557
+ QMessageBox.warning(self, 'Error', f'backward file {item} does not exist and is not empty!')
1558
+ return
1559
+
1560
+ for index, item in enumerate([
1561
+ self.alchemicalForwardLineEdit1.text(),
1562
+ self.alchemicalForwardLineEdit2.text(),
1563
+ self.alchemicalForwardLineEdit3.text(),
1564
+ self.alchemicalForwardLineEdit4.text(),
1565
+ ]):
1566
+ if (not os.path.exists(item)) and (index != 3):
1567
+ QMessageBox.warning(self, 'Error', f'file {item} does not exist!')
1568
+ return
1569
+ elif (not os.path.exists(item)) and (index == 3):
1570
+ QMessageBox.warning(self, 'Warning', f'Supposing dealing with a rigid ligand!')
1571
+ rigid_ligand = True
1572
+
1573
+ # calculate free energies
1574
+ result, errors = pTreat.alchemicalBindingFreeEnergy(files, parameters, temperature, jobType, rigid_ligand)
1575
+
1576
+ QMessageBox.about(
1577
+ self,
1578
+ 'Result',
1579
+ f'\
1580
+ Results:\n\
1581
+ ΔG(site,couple) = {result[0]:.2f} ± {errors[0]:.2f} kcal/mol\n\
1582
+ ΔG(site,c+o+a+r) = {result[1]:.2f} ± {errors[1]:.2f} kcal/mol\n\
1583
+ ΔG(bulk,decouple) = {result[2]:.2f} ± {errors[2]:.2f} kcal/mol\n\
1584
+ ΔG(bulk,c) = {result[3]:.2f} ± {errors[3]:.2f} kcal/mol\n\
1585
+ ΔG(bulk,o+a+r) = {result[4]:.2f} kcal/mol\n\
1586
+ \n\
1587
+ Standard Binding Free Energy:\n\
1588
+ ΔG(total) = {result[5]:.2f} ± {errors[5]:.2f} kcal/mol\n'
1589
+ )
1590
+
1591
+ def _showFinalResults(self):
1592
+ """calculate binding free energy and show the final results
1593
+
1594
+ Returns:
1595
+ function obj: a slot function that calculates binding free energy and show the final results
1596
+ """
1597
+
1598
+ def f():
1599
+ if self.postPMFTypeBox.currentText() == 'NAMD':
1600
+ unit = 'namd'
1601
+ elif self.postPMFTypeBox.currentText() == 'Gromacs':
1602
+ unit = 'gromacs'
1603
+
1604
+ if self.postTreatmentMainTabs.currentIndex() == 0:
1605
+ jobType = 'geometric'
1606
+ elif self.postTreatmentMainTabs.currentIndex() == 1:
1607
+ jobType = 'alchemical'
1608
+
1609
+ if jobType == 'geometric':
1610
+ self._showGeometricResults(unit)
1611
+ elif jobType == 'alchemical':
1612
+ self._showAlchemicalResults(unit)
1613
+
1614
+ return f
1615
+
1616
+ def _generateInputFiles(self):
1617
+ """generate input files for binding free energy simulation
1618
+
1619
+ Returens:
1620
+ function obj: a slot function that generates input files for binding free energy simulation
1621
+ """
1622
+
1623
+ def f():
1624
+ path = QFileDialog.getExistingDirectory(None, 'Select a directory')
1625
+ # cancel
1626
+ if path == '':
1627
+ return
1628
+
1629
+ iGenerator = inputGenerator.inputGenerator()
1630
+
1631
+ # third-party softwares and user-provided solvation boxes
1632
+ for item in [
1633
+ self.mainSettings.vmdLineEdit.text(),
1634
+ #self.mainSettings.gromacsLineEdit.text(),
1635
+ #self.mainSettings.tleapLineEdit.text(),
1636
+ self.geometricAdvancedSettings.nonStandardSolventPdbLineEdit.text(),
1637
+ self.geometricAdvancedSettings.nonStandardSolventPsfLineEdit.text()
1638
+ ]:
1639
+ if ((not os.path.exists(item)) and item != ''):
1640
+ QMessageBox.warning(self, 'Error', f'file {item} does not exist!')
1641
+ return
1642
+
1643
+ if self.preTreatmentMainTabs.currentIndex() == 0:
1644
+ # force fields
1645
+ forceFieldFiles = [self.forceFieldFilesBox.item(i).text() for i in range(self.forceFieldFilesBox.count())]
1646
+
1647
+ # NAMD files
1648
+ for item in [self.psfLineEdit.text(), self.coorLineEdit.text()] + forceFieldFiles:
1649
+ if not os.path.exists(item):
1650
+ QMessageBox.warning(self, 'Error', f'file {item} does not exist!')
1651
+ return
1652
+
1653
+ # check inputs
1654
+ try:
1655
+ float(self.temperatureLineEdit.text())
1656
+ stratification = [
1657
+ int(self.geometricAdvancedSettings.stratificationRMSDBoundLineEdit.text()),
1658
+ int(self.geometricAdvancedSettings.stratificationThetaLineEdit.text()),
1659
+ int(self.geometricAdvancedSettings.stratificationPhiLineEdit.text()),
1660
+ int(self.geometricAdvancedSettings.stratificationPsiLineEdit.text()),
1661
+ int(self.geometricAdvancedSettings.stratificationthetaLineEdit.text()),
1662
+ int(self.geometricAdvancedSettings.stratificationphiLineEdit.text()),
1663
+ int(self.geometricAdvancedSettings.stratificationRLineEdit.text()),
1664
+ int(self.geometricAdvancedSettings.stratificationRMSDUnboundLineEdit.text())
1665
+ ]
1666
+ alchemicalStratification = [
1667
+ int(self.alchemicalAdvancedSettings.boundLigandLineEdit.text()),
1668
+ int(self.alchemicalAdvancedSettings.boundRestraintsLineEdit.text()),
1669
+ int(self.alchemicalAdvancedSettings.unboundLigandLineEdit.text()),
1670
+ int(self.alchemicalAdvancedSettings.unboundRestraintsLineEdit.text())
1671
+ ]
1672
+ except:
1673
+ QMessageBox.warning(self, 'Error', f'Force constant or r* input error!')
1674
+ return
1675
+
1676
+ # job type
1677
+ if self.forceFieldCombobox.currentText() == 'CHARMM':
1678
+ forceFieldType = 'charmm'
1679
+ elif self.forceFieldCombobox.currentText() == 'Amber':
1680
+ forceFieldType = 'amber'
1681
+
1682
+ # make sure there are CHARMM FF files
1683
+ if forceFieldFiles == [] and forceFieldType == 'charmm':
1684
+ QMessageBox.warning(
1685
+ self,
1686
+ 'Error',
1687
+ f'\
1688
+ CHARMM force field files must be specified!'
1689
+ )
1690
+ return
1691
+
1692
+ if self.selectStrategyCombobox.currentText() == 'Geometric':
1693
+
1694
+ if self.selectMDEngineCombobox.currentText().lower() == 'gromacs':
1695
+ QMessageBox.warning(
1696
+ self, 'Warning', f'Please pay attention if you use the new Gromacs interface: \n\
1697
+ 1. Occationally, the atom number in complex.ndx do not correct. If so, just modify it manually; \n\
1698
+ 2. Make sure in complex.gro, the center of box is approximately at (x/2, y/2, z/2) rather than (0, 0, 0); \n\
1699
+ 3. Gromacs patched by the latest (master branch) version of Colvars is needed.'
1700
+ )
1701
+
1702
+ # for the amber force field, files of large box must be provided
1703
+ if forceFieldType == 'amber':
1704
+
1705
+ if self.geometricAdvancedSettings.nonStandardSolventPdbLineEdit.text() == '' or \
1706
+ self.geometricAdvancedSettings.nonStandardSolventPsfLineEdit.text() == '':
1707
+ QMessageBox.warning(
1708
+ self,
1709
+ 'Error',
1710
+ f'\
1711
+ Coordinate and topology files of large box must be \
1712
+ provided in "Advanced Settings"when using the Amber \
1713
+ force fields!'
1714
+ )
1715
+ return
1716
+
1717
+ if self.geometricAdvancedSettings.useGaWTMCheckbox.isChecked():
1718
+
1719
+ if self.geometricAdvancedSettings.useCUDASOAIntegrator.isChecked():
1720
+ QMessageBox.warning(self, 'Error',
1721
+ f'\
1722
+ GaWTM-eABF is not compatible with CUDASOAIntegrator in NAMD! \n'
1723
+ )
1724
+ return
1725
+
1726
+ QMessageBox.warning(self, 'Warning',
1727
+ f'\
1728
+ The feature of using GaWTM-eABF as the workhorse engine is \
1729
+ experimental! Please always use the latest devel version of NAMD!\n'
1730
+ )
1731
+
1732
+ try:
1733
+ iGenerator.generateNAMDGeometricFiles(
1734
+ path,
1735
+ self.psfLineEdit.text(),
1736
+ self.coorLineEdit.text(),
1737
+ forceFieldType,
1738
+ forceFieldFiles,
1739
+ float(self.temperatureLineEdit.text()),
1740
+ self.selectProteineLineEdit.text(),
1741
+ self.selectLigandLineEdit.text(),
1742
+ self.geometricAdvancedSettings.userDefinedDirectionLineEdit.text(),
1743
+ self.geometricAdvancedSettings.nonStandardSolventPsfLineEdit.text(),
1744
+ self.geometricAdvancedSettings.nonStandardSolventPdbLineEdit.text(),
1745
+ stratification,
1746
+ self.geometricAdvancedSettings.memProCheckbox.isChecked(),
1747
+ self.geometricAdvancedSettings.neutralizeLigOnlyCombobox.currentText(),
1748
+ self.geometricAdvancedSettings.pinDownProCheckbox.isChecked(),
1749
+ self.geometricAdvancedSettings.useOldCvCheckbox.isChecked(),
1750
+ int(self.geometricAdvancedSettings.parallelRunsLineEdit.text()),
1751
+ self.mainSettings.vmdLineEdit.text(),
1752
+ self.geometricAdvancedSettings.reflectingBoundaryCheckbox.isChecked(),
1753
+ self.selectMDEngineCombobox.currentText().lower(),
1754
+ self.geometricAdvancedSettings.OPLSMixingRuleCheckbox.isChecked(),
1755
+ self.geometricAdvancedSettings.considerRMSDCVCheckbox.isChecked(),
1756
+ self.geometricAdvancedSettings.useGaWTMCheckbox.isChecked(),
1757
+ self.geometricAdvancedSettings.useCUDASOAIntegrator.isChecked(),
1758
+ float(self.geometricAdvancedSettings.timestepLineEdit.text())
1759
+ )
1760
+ except fileParser.SelectionError:
1761
+ QMessageBox.warning(
1762
+ self,
1763
+ 'Error',
1764
+ f'\
1765
+ Selection corresponding to nothing!\n\
1766
+ Check your selection again!'
1767
+ )
1768
+ if os.path.exists(f'{path}/BFEE'):
1769
+ shutil.rmtree(f'{path}/BFEE')
1770
+ return
1771
+ except inputGenerator.DirectoryExistError:
1772
+ QMessageBox.warning(
1773
+ self,
1774
+ 'Error',
1775
+ f'\
1776
+ ./BFEE* directory already exists!'
1777
+ )
1778
+ return
1779
+ except inputGenerator.FileTypeUnknownError:
1780
+ QMessageBox.warning(
1781
+ self,
1782
+ 'Error',
1783
+ f'\
1784
+ Unkwn input file types! The following file types are supported:\n\
1785
+ psf, parm, prm7, parm7, prmtop, pdb, coor, rst, rst7, inpcrd '
1786
+ )
1787
+ return
1788
+ except PermissionError:
1789
+ QMessageBox.warning(
1790
+ self,
1791
+ 'Error',
1792
+ f'\
1793
+ Cannot read input files due to the permission reason!\n\
1794
+ Restart the program or check the authority of the files!'
1795
+ )
1796
+ return
1797
+ except Exception as e:
1798
+ print(e)
1799
+ QMessageBox.warning(
1800
+ self,
1801
+ 'Error',
1802
+ f'\
1803
+ Unknown error! The error message is: \n\
1804
+ {e}\n'
1805
+ )
1806
+ return
1807
+
1808
+ elif self.selectStrategyCombobox.currentText() == 'Alchemical':
1809
+
1810
+ try:
1811
+ iGenerator.generateNAMDAlchemicalFiles(
1812
+ path,
1813
+ self.psfLineEdit.text(),
1814
+ self.coorLineEdit.text(),
1815
+ forceFieldType,
1816
+ forceFieldFiles,
1817
+ float(self.temperatureLineEdit.text()),
1818
+ self.selectProteineLineEdit.text(),
1819
+ self.selectLigandLineEdit.text(),
1820
+ alchemicalStratification,
1821
+ self.alchemicalAdvancedSettings.doubleWideCheckbox.isChecked(),
1822
+ self.alchemicalAdvancedSettings.minBeforeSampleCheckbox.isChecked(),
1823
+ self.alchemicalAdvancedSettings.memProCheckbox.isChecked(),
1824
+ self.alchemicalAdvancedSettings.neutralizeLigOnlyCombobox.currentText(),
1825
+ self.alchemicalAdvancedSettings.pinDownProCheckbox.isChecked(),
1826
+ self.alchemicalAdvancedSettings.useOldCvCheckbox.isChecked(),
1827
+ self.mainSettings.vmdLineEdit.text(),
1828
+ self.alchemicalAdvancedSettings.OPLSMixingRuleCheckbox.isChecked(),
1829
+ self.alchemicalAdvancedSettings.considerRMSDCVCheckbox.isChecked(),
1830
+ self.alchemicalAdvancedSettings.useCUDASOAIntegrator.isChecked(),
1831
+ float(self.alchemicalAdvancedSettings.timestepLineEdit.text()),
1832
+ self.alchemicalAdvancedSettings.reEqCheckbox.isChecked()
1833
+ )
1834
+ except PermissionError:
1835
+ QMessageBox.warning(
1836
+ self,
1837
+ 'Error',
1838
+ f'\
1839
+ Cannot read input files due to the permission reason!\n\
1840
+ Restart the program or check the authority of the files!'
1841
+ )
1842
+ return
1843
+ except fileParser.SelectionError:
1844
+ QMessageBox.warning(
1845
+ self,
1846
+ 'Error',
1847
+ f'\
1848
+ Selection corresponding to nothing!\n\
1849
+ Check your selection again!'
1850
+ )
1851
+ if os.path.exists(f'{path}/BFEE'):
1852
+ shutil.rmtree(f'{path}/BFEE')
1853
+ return
1854
+ except inputGenerator.DirectoryExistError:
1855
+ QMessageBox.warning(
1856
+ self,
1857
+ 'Error',
1858
+ f'\
1859
+ ./BFEE directory already exists!'
1860
+ )
1861
+ return
1862
+ except inputGenerator.FileTypeUnknownError:
1863
+ QMessageBox.warning(
1864
+ self,
1865
+ 'Error',
1866
+ f'\
1867
+ Unkwn input file types! The following file types are supported:\n\
1868
+ psf, parm, prm7, parm7, prmtop, pdb, coor, rst, rst7, inpcrd '
1869
+ )
1870
+ return
1871
+ except Exception as e:
1872
+ print(e)
1873
+ QMessageBox.warning(
1874
+ self,
1875
+ 'Error',
1876
+ f'\
1877
+ Unknown error! The error message is: \n\
1878
+ {e}\n'
1879
+ )
1880
+ return
1881
+
1882
+ # gromacs
1883
+ if self.preTreatmentMainTabs.currentIndex() == 1:
1884
+
1885
+ QMessageBox.warning(
1886
+ self, 'Warning', ('<ol>\
1887
+ <li>Any setting in "Advanced settings" is not supported\
1888
+ when using Gromacs-formatted files as inputs!</li>\
1889
+ <li>C-rescale pressure coupling (pcoupl) is used for all simulations, \
1890
+ GROMACS version >= 2021 with Colvars module is required. \
1891
+ You may need to download it from the \
1892
+ <a href=\'https://github.com/Colvars/colvars/\'>Colvars website</a>.</li></ol>'))
1893
+
1894
+ for item in [
1895
+ self.topLineEdit.text(),
1896
+ self.gromacsPdbLineEdit.text(),
1897
+ self.gromacsLigandOnlyPdbLineEdit.text(),
1898
+ self.gromacsLigandOnlyTopLineEdit.text()
1899
+ ]:
1900
+ if not os.path.exists(item):
1901
+ QMessageBox.warning(self, 'Error', f'file {item} does not exist!')
1902
+ return
1903
+
1904
+ if self.selectStrategyCombobox.currentText() == 'Geometric':
1905
+ try:
1906
+ iGenerator.generateGromacsGeometricFiles(
1907
+ path=path,
1908
+ topFile=self.topLineEdit.text(),
1909
+ pdbFile=self.gromacsPdbLineEdit.text(),
1910
+ pdbFileFormat=self.gromacsStructureFileFormatCombobox.currentText(),
1911
+ ligandOnlyTopFile=self.gromacsLigandOnlyTopLineEdit.text(),
1912
+ ligandOnlyPdbFile=self.gromacsLigandOnlyPdbLineEdit.text(),
1913
+ ligandOnlyPdbFileFormat=self.gromacsLigandOnlyStructureFileFormatCombobox.currentText(),
1914
+ selectionPro=self.selectProteineLineEdit.text(),
1915
+ selectionLig=self.selectLigandLineEdit.text(),
1916
+ temperature=float(self.temperatureLineEdit.text())
1917
+ )
1918
+ except inputGenerator.DirectoryExistError:
1919
+ QMessageBox.warning(
1920
+ self,
1921
+ 'Error',
1922
+ f'\
1923
+ ./BFEE directory already exists!'
1924
+ )
1925
+ return
1926
+ except BFEEGromacs.SelectionError:
1927
+ QMessageBox.warning(
1928
+ self,
1929
+ 'Error',
1930
+ f'\
1931
+ Selection corresponding to nothing!\n\
1932
+ Check your selection again!'
1933
+ )
1934
+ if os.path.exists(f'{path}/BFEE'):
1935
+ shutil.rmtree(f'{path}/BFEE')
1936
+ return
1937
+ except Exception as e:
1938
+ print(e)
1939
+ QMessageBox.warning(
1940
+ self,
1941
+ 'Error',
1942
+ f'\
1943
+ Unknown error!'
1944
+ )
1945
+ return
1946
+ elif self.selectStrategyCombobox.currentText() == 'Alchemical':
1947
+ QMessageBox.warning(self, 'Error', f'Alchemical route is not supported using Gromacs!')
1948
+ return
1949
+
1950
+ QMessageBox.information(self, 'Input generation', f'Input files have been generated successfully!')
1951
+
1952
+ del iGenerator
1953
+
1954
+ return f
1955
+
1956
+ def _mergePMFs(self):
1957
+ """merge a series of overlapped pmfs
1958
+
1959
+ Returns:
1960
+ function obj: a slot function that merges a series of overlapped pmfs
1961
+ """
1962
+
1963
+ def f():
1964
+ if self.mergePmfBox.count() == 0:
1965
+ QMessageBox.warning(self, 'Warning', f'Warning, no PMF selected!')
1966
+ return
1967
+
1968
+ path, _ = QFileDialog.getSaveFileName(None, 'Set the name of merged PMF')
1969
+
1970
+ pmfFiles = [self.mergePmfBox.item(i).text() for i in range(self.mergePmfBox.count())]
1971
+
1972
+ if not ploter.isGaWTM(pmfFiles):
1973
+ pmfs = [ploter.readPMF(item) for item in pmfFiles]
1974
+ else:
1975
+ pmfFiles = [item for item in pmfFiles if not item.endswith('.reweightamd1.cumulant.pmf')]
1976
+ try:
1977
+ pmfs = [ploter.correctGaWTM(item) for item in pmfFiles]
1978
+ except ploter.NoCorrectionFileError:
1979
+ QMessageBox.warning(self, 'Error', f'A PMF file does not have corresponding correction!')
1980
+ return
1981
+
1982
+ mergedPMF = ploter.mergePMF(pmfs)
1983
+ ploter.writePMF(path, mergedPMF)
1984
+ QMessageBox.information(self, 'Merge PMFs', f'PMF merged successfully!')
1985
+ return f
1986
+
1987
+ def _plotPMFs(self):
1988
+ """plot a series of overlapped pmfs
1989
+
1990
+ Returns:
1991
+ function obj: a slot function that plots a series of overlapped pmfs
1992
+ """
1993
+
1994
+ def f():
1995
+ if self.plotPmfBox.count() == 0:
1996
+ QMessageBox.warning(self, 'Warning', f'Warning, no PMF selected!')
1997
+ return
1998
+
1999
+ pmfFiles = [self.plotPmfBox.item(i).text() for i in range(self.plotPmfBox.count())]
2000
+
2001
+ if not ploter.isGaWTM(pmfFiles):
2002
+ pmfs = [ploter.readPMF(item) for item in pmfFiles]
2003
+ else:
2004
+ pmfFiles = [item for item in pmfFiles if not item.endswith('.reweightamd1.cumulant.pmf')]
2005
+ try:
2006
+ pmfs = [ploter.correctGaWTM(item) for item in pmfFiles]
2007
+ except ploter.NoCorrectionFileError:
2008
+ QMessageBox.warning(self, 'Error', f'A PMF file does not have corresponding correction!')
2009
+ return
2010
+
2011
+ mergedPMF = ploter.mergePMF(pmfs)
2012
+ ploter.plotPMF(mergedPMF)
2013
+ return f
2014
+
2015
+ def _plotRMSDConvergence(self):
2016
+ """plot time evolution of PMF rmsd with respect to zero array
2017
+
2018
+ Returns:
2019
+ function obj: a slot function that plots time evolution of PMF rmsd with respect to zero array
2020
+ """
2021
+
2022
+ def f():
2023
+ path = self.plotPmfConvergenceBox.text()
2024
+ if not os.path.exists(path):
2025
+ QMessageBox.warning(self, 'Error', f'file {path} does not exist!')
2026
+ return
2027
+
2028
+ rmsds = ploter.parseHistFile(path)
2029
+ ploter.plotConvergence(rmsds)
2030
+ return f
2031
+
2032
+ def _plotHysteresis(self):
2033
+ """plot hysteresis between forward and backward alchemical transformations
2034
+
2035
+ Returns:
2036
+ function obj: a slot function that plot hysteresis between forward and backward alchemical transformations
2037
+ """
2038
+
2039
+ def f():
2040
+ forwardFilePath = self.plotHysteresisForwardLineEdit.text()
2041
+ backwardFilePath = self.plotHysteresisBackwardLineEdit.text()
2042
+ if not os.path.exists(forwardFilePath):
2043
+ QMessageBox.warning(self, 'Error', f'file {forwardFilePath} does not exist!')
2044
+ return
2045
+ if not os.path.exists(backwardFilePath):
2046
+ QMessageBox.warning(self, 'Error', f'file {backwardFilePath} does not exist!')
2047
+ return
2048
+
2049
+ forwardPostfix = os.path.splitext(forwardFilePath)[-1]
2050
+ backwardPostfix = os.path.splitext(backwardFilePath)[-1]
2051
+
2052
+ if forwardPostfix != '.fepout' and forwardPostfix != '.log' \
2053
+ and backwardPostfix != '.fepout' and forwardPostfix != '.log':
2054
+ QMessageBox.warning(self, 'Error', f'File type not correct!')
2055
+ return
2056
+
2057
+ if forwardPostfix != backwardPostfix:
2058
+ QMessageBox.warning(self, 'Error', f'File types of forward and backward simulations are not the same!')
2059
+ return
2060
+
2061
+ pTreat = postTreatment.postTreatment(float(self.alchemicalPostTemperatureLineEdit.text()), 'namd', 'alchemical')
2062
+ if forwardPostfix == '.fepout':
2063
+ forwardProfile = np.transpose(pTreat._fepoutFile(forwardFilePath))
2064
+ backwardProfile = np.transpose(pTreat._fepoutFile(backwardFilePath))
2065
+ backwardProfile[:,1] *= -1
2066
+ elif forwardPostfix == '.log':
2067
+ forwardProfile = np.transpose(pTreat._tiLogFile(forwardFilePath))
2068
+ backwardProfile = np.transpose(pTreat._tiLogFile(backwardFilePath))
2069
+ ploter.plotHysteresis(forwardProfile, backwardProfile)
2070
+ return f
2071
+
2072
+
2073
+ def _initSingalsSlots(self):
2074
+ """initialize (connect) singals and slots
2075
+ """
2076
+
2077
+ # pre-treatment tab
2078
+ self.selectStrategyAdvancedButton.clicked.connect(self._advancedSettings(self.selectStrategyCombobox))
2079
+ self.preTreatmentMainTabs.currentChanged.connect(self._changeStrategySettingStateForOldGromacs)
2080
+
2081
+ # NAMD tab
2082
+ self.psfButton.clicked.connect(commonSlots.openFileDialog('psf/parm', self.psfLineEdit))
2083
+ self.coorButton.clicked.connect(commonSlots.openFileDialog('pdb/rst', self.coorLineEdit))
2084
+
2085
+ # force field selection
2086
+ self.forceFieldCombobox.currentTextChanged.connect(self._changeFFButtonState)
2087
+ self.forceFieldAddButton.clicked.connect(commonSlots.openFilesDialog('prm', self.forceFieldFilesBox))
2088
+ self.forceFieldClearButton.clicked.connect(self.forceFieldFilesBox.clear)
2089
+
2090
+ # MD engine
2091
+ self.selectMDEngineCombobox.currentTextChanged.connect(self._changeStrategySettingState)
2092
+
2093
+ # gromacs tab
2094
+ self.gromacsPdbButton.clicked.connect(commonSlots.openFileDialog('pdb', self.gromacsPdbLineEdit))
2095
+ self.topButton.clicked.connect(commonSlots.openFileDialog('top', self.topLineEdit))
2096
+ self.gromacsLigandOnlyPdbButton.clicked.connect(commonSlots.openFileDialog('pdb', self.gromacsLigandOnlyPdbLineEdit))
2097
+ self.gromacsLigandOnlyTopButton.clicked.connect(commonSlots.openFileDialog('top', self.gromacsLigandOnlyTopLineEdit))
2098
+
2099
+ # geometric tab
2100
+ self.rmsdBoundButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.rmsdBoundLineEdit))
2101
+ self.rmsdUnboundButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.rmsdUnboundLineEdit))
2102
+ self.ThetaButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.ThetaLineEdit))
2103
+ self.PhiButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.PhiLineEdit))
2104
+ self.PsiButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.PsiLineEdit))
2105
+ self.thetaButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.thetaLineEdit))
2106
+ self.phiButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.phiLineEdit))
2107
+ self.rButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.rLineEdit))
2108
+
2109
+ # alchemical tab
2110
+ self.alchemicalForwardButton1.clicked.connect(commonSlots.openFileDialog('log', self.alchemicalForwardLineEdit1))
2111
+ self.alchemicalBackwardButton1.clicked.connect(commonSlots.openFileDialog('log', self.alchemicalBackwardLineEdit1))
2112
+ self.alchemicalForwardButton2.clicked.connect(commonSlots.openFileDialog('log', self.alchemicalForwardLineEdit2))
2113
+ self.alchemicalBackwardButton2.clicked.connect(commonSlots.openFileDialog('log', self.alchemicalBackwardLineEdit2))
2114
+ self.alchemicalForwardButton3.clicked.connect(commonSlots.openFileDialog('fepout', self.alchemicalForwardLineEdit3))
2115
+ self.alchemicalBackwardButton3.clicked.connect(commonSlots.openFileDialog('fepout', self.alchemicalBackwardLineEdit3))
2116
+ self.alchemicalForwardButton4.clicked.connect(commonSlots.openFileDialog('fepout', self.alchemicalForwardLineEdit4))
2117
+ self.alchemicalBackwardButton4.clicked.connect(commonSlots.openFileDialog('fepout', self.alchemicalBackwardLineEdit4))
2118
+
2119
+ # generate input files
2120
+ self.generateInputButton.clicked.connect(self._generateInputFiles())
2121
+
2122
+ # calculate binding free energy
2123
+ self.calculateButton.clicked.connect(self._showFinalResults())
2124
+
2125
+ # quick-plot tab
2126
+ self.plotPmfAddButton.clicked.connect(commonSlots.openFilesDialog('pmf', self.plotPmfBox))
2127
+ self.plotPmfClearButton.clicked.connect(self.plotPmfBox.clear)
2128
+ self.plotPmfPlotButton.clicked.connect(self._plotPMFs())
2129
+ self.mergePmfAddButton.clicked.connect(commonSlots.openFilesDialog('pmf', self.mergePmfBox))
2130
+ self.mergePmfClearButton.clicked.connect(self.mergePmfBox.clear)
2131
+ self.mergePmfmergeButton.clicked.connect(self._mergePMFs())
2132
+ self.plotPmfConvergenceBrowseButton.clicked.connect(commonSlots.openFileDialog('pmf', self.plotPmfConvergenceBox))
2133
+ self.plotPmfConvergencePlotButton.clicked.connect(self._plotRMSDConvergence())
2134
+ self.plotHysteresisForwardButton.clicked.connect(commonSlots.openFileDialog('fepout/log', self.plotHysteresisForwardLineEdit))
2135
+ self.plotHysteresisBackwardButton.clicked.connect(commonSlots.openFileDialog('fepout/log', self.plotHysteresisBackwardLineEdit))
2136
+ self.plotHysteresisPlotButton.clicked.connect(self._plotHysteresis())