bfee2 3.1.1.post1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. BFEE2/__init__.py +0 -0
  2. BFEE2/commonTools/__init__.py +0 -0
  3. BFEE2/commonTools/commonSlots.py +48 -0
  4. BFEE2/commonTools/fileParser.py +327 -0
  5. BFEE2/commonTools/ploter.py +218 -0
  6. BFEE2/doc/Doc.pdf +0 -0
  7. BFEE2/doc/__init__.py +1 -0
  8. BFEE2/gui.py +2785 -0
  9. BFEE2/inputGenerator.py +2949 -0
  10. BFEE2/postTreatment.py +676 -0
  11. BFEE2/templates_gromacs/000.colvars.template +37 -0
  12. BFEE2/templates_gromacs/000.generate_tpr_sh.template +31 -0
  13. BFEE2/templates_gromacs/000.mdp.template +74 -0
  14. BFEE2/templates_gromacs/001.colvars.template +76 -0
  15. BFEE2/templates_gromacs/001.generate_tpr_sh.template +31 -0
  16. BFEE2/templates_gromacs/001.mdp.template +73 -0
  17. BFEE2/templates_gromacs/001.readme.template +1 -0
  18. BFEE2/templates_gromacs/002.colvars.template +101 -0
  19. BFEE2/templates_gromacs/002.generate_tpr_sh.template +31 -0
  20. BFEE2/templates_gromacs/002.mdp.template +73 -0
  21. BFEE2/templates_gromacs/003.colvars.template +125 -0
  22. BFEE2/templates_gromacs/003.generate_tpr_sh.template +36 -0
  23. BFEE2/templates_gromacs/003.mdp.template +73 -0
  24. BFEE2/templates_gromacs/004.colvars.template +148 -0
  25. BFEE2/templates_gromacs/004.generate_tpr_sh.template +37 -0
  26. BFEE2/templates_gromacs/004.mdp.template +74 -0
  27. BFEE2/templates_gromacs/005.colvars.template +170 -0
  28. BFEE2/templates_gromacs/005.generate_tpr_sh.template +38 -0
  29. BFEE2/templates_gromacs/005.mdp.template +74 -0
  30. BFEE2/templates_gromacs/006.colvars.template +192 -0
  31. BFEE2/templates_gromacs/006.generate_tpr_sh.template +39 -0
  32. BFEE2/templates_gromacs/006.mdp.template +74 -0
  33. BFEE2/templates_gromacs/007.colvars.template +210 -0
  34. BFEE2/templates_gromacs/007.generate_tpr_sh.template +40 -0
  35. BFEE2/templates_gromacs/007.mdp.template +73 -0
  36. BFEE2/templates_gromacs/007_eq.colvars.template +169 -0
  37. BFEE2/templates_gromacs/007_eq.generate_tpr_sh.template +64 -0
  38. BFEE2/templates_gromacs/007_min.mdp.template +62 -0
  39. BFEE2/templates_gromacs/008.colvars.template +42 -0
  40. BFEE2/templates_gromacs/008.generate_tpr_sh.template +31 -0
  41. BFEE2/templates_gromacs/008.mdp.template +74 -0
  42. BFEE2/templates_gromacs/008_eq.colvars.template +14 -0
  43. BFEE2/templates_gromacs/008_eq.generate_tpr_sh.template +31 -0
  44. BFEE2/templates_gromacs/BFEEGromacs.py +1268 -0
  45. BFEE2/templates_gromacs/__init__.py +0 -0
  46. BFEE2/templates_gromacs/find_min_max.awk +27 -0
  47. BFEE2/templates_namd/__init__.py +0 -0
  48. BFEE2/templates_namd/configTemplate.py +1152 -0
  49. BFEE2/templates_namd/fep.tcl +299 -0
  50. BFEE2/templates_namd/fep_lddm.tcl +312 -0
  51. BFEE2/templates_namd/scriptTemplate.py +304 -0
  52. BFEE2/templates_namd/solvate.tcl +9 -0
  53. BFEE2/templates_namd/solvate_mem.tcl +9 -0
  54. BFEE2/templates_namd/updateCenters.py +312 -0
  55. BFEE2/templates_readme/Readme_Gromacs_Geometrical.txt +25 -0
  56. BFEE2/templates_readme/Readme_NAMD_Alchemical.txt +20 -0
  57. BFEE2/templates_readme/Readme_NAMD_Geometrical.txt +34 -0
  58. BFEE2/templates_readme/__init__.py +1 -0
  59. BFEE2/templates_readme/rags.py +187 -0
  60. BFEE2/third_party/__init__.py +0 -0
  61. BFEE2/third_party/py_bar.py +585 -0
  62. BFEE2/version.py +4 -0
  63. bfee2-3.1.1.post1.data/scripts/BFEE2Gui.py +19 -0
  64. bfee2-3.1.1.post1.dist-info/METADATA +86 -0
  65. bfee2-3.1.1.post1.dist-info/RECORD +68 -0
  66. bfee2-3.1.1.post1.dist-info/WHEEL +5 -0
  67. bfee2-3.1.1.post1.dist-info/licenses/LICENSE +677 -0
  68. bfee2-3.1.1.post1.dist-info/top_level.txt +1 -0
BFEE2/gui.py ADDED
@@ -0,0 +1,2785 @@
1
+ # the GUI of new BFEE
2
+
3
+ import os
4
+ import shutil
5
+ import sys
6
+ import webbrowser
7
+ import requests
8
+ import re
9
+
10
+ import numpy as np
11
+ # use appdirs to manage persistent configuration
12
+ from appdirs import user_config_dir
13
+ from PySide6 import QtCore
14
+ from PySide6.QtGui import QFont, QIcon, QAction
15
+ from PySide6.QtWidgets import ( QApplication, QCheckBox, QComboBox,
16
+ QFileDialog, QGridLayout, QGroupBox,
17
+ QHBoxLayout, QLabel, QLineEdit, QListWidget,
18
+ QMainWindow, QMessageBox, QPushButton,
19
+ QSplitter, QTabWidget, QToolBar, QVBoxLayout,
20
+ QWidget, QSpacerItem, QProgressDialog)
21
+
22
+ import BFEE2.inputGenerator as inputGenerator
23
+ import BFEE2.postTreatment as postTreatment
24
+ import BFEE2.templates_gromacs.BFEEGromacs as BFEEGromacs
25
+ from BFEE2.commonTools import commonSlots, fileParser, ploter
26
+ from BFEE2.templates_readme import rags
27
+
28
+ try:
29
+ import importlib.resources as pkg_resources
30
+ except ImportError:
31
+ # Try backported to PY<37 `importlib_resources`.
32
+ import importlib_resources as pkg_resources
33
+
34
+ import BFEE2.version
35
+ from BFEE2 import doc
36
+
37
+ __PROGRAM_NAME__ = f'BFEEstimator v{BFEE2.version.__VERSION__}'
38
+ __NAMD_VERSION__ = f'v{BFEE2.version.__NAMD_VERSION__}'
39
+ __GMX_VERSION__ = f'{BFEE2.version.__GMX_VERSION__}'
40
+
41
+ class mainSettings(QWidget):
42
+ """settings in the menubar
43
+ set pathes of third-party softwares (VMD, gmx and tleap)
44
+ """
45
+
46
+ def __init__(self):
47
+ super().__init__()
48
+ self.config_dir = user_config_dir('BFEE2', 'chinfo')
49
+ # test if config directory exists
50
+ if not os.path.exists(self.config_dir):
51
+ # create it if not exists
52
+ os.makedirs(self.config_dir)
53
+ self._initUI()
54
+ self._initSingalsSlots()
55
+ self.setWindowTitle('Settings')
56
+ self._readConfig()
57
+ #self.setGeometry(0,0,0,0)
58
+ #self.show()
59
+
60
+ def _initUI(self):
61
+ """settings GUI
62
+ """
63
+
64
+ self.mainLayout = QVBoxLayout()
65
+
66
+ # third party softward
67
+ self.thirdPartySoftware = QGroupBox('Third party software:')
68
+ self.thirdPartySoftwareGridLayout = QGridLayout()
69
+
70
+ # vmd
71
+ self.vmdLabel = QLabel('VMD:')
72
+ self.vmdLineEdit = QLineEdit()
73
+ self.vmdButton = QPushButton('Browse')
74
+ self.thirdPartySoftwareGridLayout.addWidget(self.vmdLabel, 0, 0)
75
+ self.thirdPartySoftwareGridLayout.addWidget(self.vmdLineEdit, 0, 1)
76
+ self.thirdPartySoftwareGridLayout.addWidget(self.vmdButton, 0, 2)
77
+
78
+ # online AI assistant
79
+ self.onlineAIService = QGroupBox('Online AI Service:')
80
+ self.onlineAIServiceGridLayout = QGridLayout()
81
+
82
+ # OpenRouter
83
+ self.openRouterAPILabel = QLabel('OpenRouter API:')
84
+ self.openRouterAPILineEdit = QLineEdit('')
85
+ self.openRouterModelLabel = QLabel('OpenRouter Model:')
86
+ self.openRouterModelLineEdit = QLineEdit('')
87
+ self.openRouterTemperatureLabel = QLabel('Temperature:')
88
+ self.openRouterTemperatureLineEdit = QLineEdit('')
89
+ self.openRouterTopPLabel = QLabel('Top P:')
90
+ self.openRouterTopPLineEdit = QLineEdit('')
91
+ self.onlineAIServiceGridLayout.addWidget(self.openRouterAPILabel, 0, 0)
92
+ self.onlineAIServiceGridLayout.addWidget(self.openRouterAPILineEdit, 0, 1)
93
+ self.onlineAIServiceGridLayout.addWidget(self.openRouterModelLabel, 1, 0)
94
+ self.onlineAIServiceGridLayout.addWidget(self.openRouterModelLineEdit, 1, 1)
95
+ self.onlineAIServiceGridLayout.addWidget(self.openRouterTemperatureLabel, 2, 0)
96
+ self.onlineAIServiceGridLayout.addWidget(self.openRouterTemperatureLineEdit, 2, 1)
97
+ self.onlineAIServiceGridLayout.addWidget(self.openRouterTopPLabel, 3, 0)
98
+ self.onlineAIServiceGridLayout.addWidget(self.openRouterTopPLineEdit, 3, 1)
99
+
100
+ # OK and Cancel
101
+ self.settingsButtonLayout = QHBoxLayout()
102
+ self.settingsOKButton = QPushButton('OK')
103
+ self.settingsButtonLayout.addWidget(QSplitter())
104
+ self.settingsButtonLayout.addWidget(self.settingsOKButton)
105
+
106
+ self.thirdPartySoftware.setLayout(self.thirdPartySoftwareGridLayout)
107
+ self.onlineAIService.setLayout(self.onlineAIServiceGridLayout)
108
+
109
+ self.mainLayout.addWidget(self.thirdPartySoftware)
110
+ self.mainLayout.addWidget(self.onlineAIService)
111
+ self.mainLayout.addLayout(self.settingsButtonLayout)
112
+ self.setLayout(self.mainLayout)
113
+
114
+ def _initSingalsSlots(self):
115
+ """initialize singals and slots
116
+ """
117
+ self.vmdButton.clicked.connect(commonSlots.openFileDialog('exe', self.vmdLineEdit))
118
+ #self.gromacsButton.clicked.connect(commonSlots.openFileDialog('exe', self.gromacsLineEdit))
119
+ #self.tleapButton.clicked.connect(commonSlots.openFileDialog('exe', self.tleapLineEdit))
120
+ self.settingsOKButton.clicked.connect(self._OKSlot())
121
+
122
+ def _readConfig(self):
123
+ """read the config saving paths for third-party softwares
124
+ """
125
+ if not os.path.exists(f'{self.config_dir}/3rdSoft.ini'):
126
+ return
127
+
128
+ with open(f'{self.config_dir}/3rdSoft.ini', 'r') as cFile:
129
+ self.vmdLineEdit.setText(cFile.readline().strip())
130
+ self.openRouterAPILineEdit.setText(cFile.readline().strip())
131
+ self.openRouterModelLineEdit.setText(cFile.readline().strip())
132
+ self.openRouterTemperatureLineEdit.setText(cFile.readline().strip())
133
+ self.openRouterTopPLineEdit.setText(cFile.readline().strip())
134
+
135
+ def _writeConfig(self):
136
+ """write the config saving paths for third-party softwares
137
+ """
138
+
139
+ with open(f'{self.config_dir}/3rdSoft.ini', 'w') as cFile:
140
+ cFile.write(self.vmdLineEdit.text() + '\n')
141
+ cFile.write(self.openRouterAPILineEdit.text() + '\n')
142
+ cFile.write(self.openRouterModelLineEdit.text() + '\n')
143
+ cFile.write(self.openRouterTemperatureLineEdit.text() + '\n')
144
+ cFile.write(self.openRouterTopPLineEdit.text() + '\n')
145
+
146
+ def _OKSlot(self):
147
+ """the slot corresponding the OK button
148
+ """
149
+ def f():
150
+ self._writeConfig()
151
+ self.close()
152
+ return f
153
+
154
+ class geometricAdvancedSettings(QWidget):
155
+ """advanced settings for the geometric route
156
+ set pulling direction, non-standard solvent
157
+ and number of stratification windows
158
+ """
159
+
160
+ def __init__(self):
161
+ super().__init__()
162
+ self._initUI()
163
+ self._initSingalsSlots()
164
+ self.setWindowTitle('Geometric advanced settings')
165
+ self.setGeometry(0,0,0,0)
166
+ #self.show()
167
+
168
+ def _initUI(self):
169
+ """initialize UI for the geometric advanced settings
170
+ """
171
+
172
+ self.mainLayout = QVBoxLayout()
173
+
174
+ # user-defined pulling direction
175
+ self.userDefinedDirection = QGroupBox('User-defined separation direction')
176
+ self.userDefinedDirectionLayout = QHBoxLayout()
177
+
178
+ self.userDefinedDirectionLabel = QLabel('Reference:')
179
+ self.userDefinedDirectionLineEdit = QLineEdit()
180
+ self.userDefinedDirectionLayout.addWidget(self.userDefinedDirectionLabel)
181
+ self.userDefinedDirectionLayout.addWidget(self.userDefinedDirectionLineEdit)
182
+
183
+ self.userDefinedDirection.setLayout(self.userDefinedDirectionLayout)
184
+
185
+ # non-standard solvent
186
+ self.nonStandardSolvent = QGroupBox('User-provided large box')
187
+ self.nonStandardSolventLayout = QGridLayout()
188
+
189
+ self.nonStandardSolventPsfLabel = QLabel('psf/parm file:')
190
+ self.nonStandardSolventPsfLineEdit = QLineEdit()
191
+ self.nonStandardSolventPsfButton = QPushButton('Browse')
192
+ self.nonStandardSolventPdbLabel = QLabel('pdb/rst7 file:')
193
+ self.nonStandardSolventPdbLineEdit = QLineEdit()
194
+ self.nonStandardSolventPdbButton = QPushButton('Browse')
195
+ self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPsfLabel, 0, 0)
196
+ self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPsfLineEdit, 0, 1)
197
+ self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPsfButton, 0, 2)
198
+ self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPdbLabel, 1, 0)
199
+ self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPdbLineEdit, 1, 1)
200
+ self.nonStandardSolventLayout.addWidget(self.nonStandardSolventPdbButton, 1, 2)
201
+
202
+ self.nonStandardSolvent.setLayout(self.nonStandardSolventLayout)
203
+
204
+ # stratification
205
+ self.stratification = QGroupBox('Stratification (number of strata)')
206
+ self.stratificationLayout = QGridLayout()
207
+
208
+ self.stratificationRMSDBoundLabel = QLabel('RMSD(Bound):')
209
+ self.stratificationRMSDBoundLineEdit = QLineEdit('1')
210
+ self.stratificationTheta = QLabel('Theta:')
211
+ self.stratificationThetaLineEdit = QLineEdit('1')
212
+ self.stratificationPhiLabel = QLabel('Phi:')
213
+ self.stratificationPhiLineEdit = QLineEdit('1')
214
+ self.stratificationPsiLabel = QLabel('Psi:')
215
+ self.stratificationPsiLineEdit = QLineEdit('1')
216
+ self.stratificationthetaLabel = QLabel('theta:')
217
+ self.stratificationthetaLineEdit = QLineEdit('1')
218
+ self.stratificationphiLabel = QLabel('phi:')
219
+ self.stratificationphiLineEdit = QLineEdit('1')
220
+ self.stratificationRLabel = QLabel('r:')
221
+ self.stratificationRLineEdit = QLineEdit('1')
222
+ self.stratificationRMSDUnboundLabel = QLabel('RMSD(Unbound):')
223
+ self.stratificationRMSDUnboundLineEdit = QLineEdit('1')
224
+
225
+ self.stratificationLayout.addWidget(self.stratificationRMSDBoundLabel, 0, 0)
226
+ self.stratificationLayout.addWidget(self.stratificationRMSDBoundLineEdit, 0, 1)
227
+ self.stratificationLayout.addWidget(self.stratificationTheta, 0, 2)
228
+ self.stratificationLayout.addWidget(self.stratificationThetaLineEdit, 0, 3)
229
+ self.stratificationLayout.addWidget(self.stratificationPhiLabel, 0, 4)
230
+ self.stratificationLayout.addWidget(self.stratificationPhiLineEdit, 0, 5)
231
+ self.stratificationLayout.addWidget(self.stratificationPsiLabel, 0, 6)
232
+ self.stratificationLayout.addWidget(self.stratificationPsiLineEdit, 0, 7)
233
+ self.stratificationLayout.addWidget(self.stratificationthetaLabel, 1, 0)
234
+ self.stratificationLayout.addWidget(self.stratificationthetaLineEdit, 1, 1)
235
+ self.stratificationLayout.addWidget(self.stratificationphiLabel, 1, 2)
236
+ self.stratificationLayout.addWidget(self.stratificationphiLineEdit, 1, 3)
237
+ self.stratificationLayout.addWidget(self.stratificationRLabel, 1, 4)
238
+ self.stratificationLayout.addWidget(self.stratificationRLineEdit, 1, 5)
239
+ self.stratificationLayout.addWidget(self.stratificationRMSDUnboundLabel, 1, 6)
240
+ self.stratificationLayout.addWidget(self.stratificationRMSDUnboundLineEdit, 1, 7)
241
+
242
+ self.stratification.setLayout(self.stratificationLayout)
243
+
244
+ # compatibility
245
+ self.compatibility = QGroupBox('Compatibility')
246
+ self.compatibilityLayout = QGridLayout()
247
+
248
+ self.pinDownProCheckbox = QCheckBox('Pinning down the protein')
249
+ self.pinDownProCheckbox.setChecked(True)
250
+
251
+ self.useOldCvCheckbox = QCheckBox('Use quaternion-based CVs')
252
+ self.useOldCvCheckbox.setChecked(False)
253
+
254
+ self.reflectingBoundaryCheckbox = QCheckBox('Use reflecting boundary')
255
+ self.reflectingBoundaryCheckbox.setChecked(True)
256
+
257
+ self.useCUDASOAIntegrator = QCheckBox('Use CUDASOA integrator')
258
+ self.useCUDASOAIntegrator.setChecked(True)
259
+
260
+ self.compatibilityLayout.addWidget(self.pinDownProCheckbox, 0, 0)
261
+ self.compatibilityLayout.addWidget(self.useOldCvCheckbox, 0, 1)
262
+ self.compatibilityLayout.addWidget(self.reflectingBoundaryCheckbox, 1, 0)
263
+ self.compatibilityLayout.addWidget(self.useCUDASOAIntegrator, 1, 1)
264
+ self.compatibility.setLayout(self.compatibilityLayout)
265
+
266
+ # force field settings
267
+ self.FFSettings = QGroupBox('Force field settings')
268
+ self.FFSettingsLayout = QHBoxLayout()
269
+
270
+ self.OPLSMixingRuleCheckbox = QCheckBox('OPLS mixing rules')
271
+ self.OPLSMixingRuleCheckbox.setChecked(False)
272
+
273
+ self.timestepLayout = QHBoxLayout()
274
+ self.timestepLabel = QLabel(' Timestep:')
275
+ self.timestepLineEdit = QLineEdit('2.0')
276
+ self.timestepLayout.addWidget(self.timestepLabel)
277
+ self.timestepLayout.addWidget(self.timestepLineEdit)
278
+
279
+ self.FFSettingsLayout.addWidget(self.OPLSMixingRuleCheckbox)
280
+ self.FFSettingsLayout.addLayout(self.timestepLayout)
281
+ self.FFSettings.setLayout(self.FFSettingsLayout)
282
+
283
+ # strategy settings
284
+ self.strategy = QGroupBox('Strategy settings')
285
+ self.strategyLayout = QHBoxLayout()
286
+
287
+ self.considerRMSDCVCheckbox = QCheckBox('Take into account RMSD CV')
288
+ self.considerRMSDCVCheckbox.setChecked(True)
289
+
290
+ self.useGaWTMCheckbox = QCheckBox('Use GaWTM-eABF')
291
+ self.useGaWTMCheckbox.setChecked(False)
292
+
293
+ self.strategyLayout.addWidget(self.considerRMSDCVCheckbox)
294
+ self.strategyLayout.addWidget(self.useGaWTMCheckbox)
295
+ self.strategy.setLayout(self.strategyLayout)
296
+
297
+ # membrane protein
298
+ self.modeling = QGroupBox('Modeling (avaiable for CHARMM FF)')
299
+ self.modelingLayout = QVBoxLayout()
300
+
301
+ self.memProCheckbox = QCheckBox('Membrane protein')
302
+ self.memProCheckbox.setChecked(False)
303
+
304
+ self.neutralizeLigOnlyLayout = QHBoxLayout()
305
+ self.neutralizeLigOnlyLabel = QLabel('Auto-neutralize ligand-only system by:')
306
+ self.neutralizeLigOnlyCombobox = QComboBox()
307
+ self.neutralizeLigOnlyCombobox.addItem('NaCl')
308
+ self.neutralizeLigOnlyCombobox.addItem('KCl')
309
+ self.neutralizeLigOnlyCombobox.addItem('CaCl2')
310
+ self.neutralizeLigOnlyCombobox.addItem('None')
311
+ self.neutralizeLigOnlyLayout.addWidget(self.neutralizeLigOnlyLabel)
312
+ self.neutralizeLigOnlyLayout.addWidget(self.neutralizeLigOnlyCombobox)
313
+
314
+ self.modelingLayout.addWidget(self.memProCheckbox)
315
+ self.modelingLayout.addLayout(self.neutralizeLigOnlyLayout)
316
+ self.modeling.setLayout(self.modelingLayout)
317
+
318
+ # parallel runs for error estimation
319
+ self.parallelRuns = QGroupBox('Parallel runs')
320
+ self.parallelRunsLayout = QHBoxLayout()
321
+
322
+ self.parallelRunsLabel = QLabel('Number of parallel runs: ')
323
+ self.parallelRunsLineEdit = QLineEdit('1')
324
+
325
+ self.parallelRunsLayout.addWidget(self.parallelRunsLabel)
326
+ self.parallelRunsLayout.addWidget(self.parallelRunsLineEdit)
327
+ self.parallelRuns.setLayout(self.parallelRunsLayout)
328
+
329
+
330
+ self.geometricAdvancedSettingsButtonLayout = QHBoxLayout()
331
+ self.geometricAdvancedSettingsOKButton = QPushButton('OK')
332
+ #self.geometricAdvancedSettingsCancelButton = QPushButton('Cancel')
333
+ self.geometricAdvancedSettingsButtonLayout.addWidget(QSplitter())
334
+ self.geometricAdvancedSettingsButtonLayout.addWidget(self.geometricAdvancedSettingsOKButton)
335
+ #self.geometricAdvancedSettingsButtonLayout.addWidget(self.geometricAdvancedSettingsCancelButton)
336
+
337
+ self.mainLayout.addWidget(self.userDefinedDirection)
338
+ self.mainLayout.addWidget(self.nonStandardSolvent)
339
+ self.mainLayout.addWidget(self.stratification)
340
+ self.mainLayout.addWidget(self.compatibility)
341
+ self.mainLayout.addWidget(self.FFSettings)
342
+ self.mainLayout.addWidget(self.strategy)
343
+ self.mainLayout.addWidget(self.modeling)
344
+ self.mainLayout.addWidget(self.parallelRuns)
345
+ self.mainLayout.addLayout(self.geometricAdvancedSettingsButtonLayout)
346
+ self.setLayout(self.mainLayout)
347
+
348
+ # below are slow functions
349
+ def _toggleGaWTMBox(self, checked):
350
+ """when enable GaWTM, restraint simulations and considering RMSD are not needed, and
351
+ re-equilbration and double-wide simulations are necessary.
352
+ """
353
+
354
+ if checked:
355
+ self.useCUDASOAIntegrator.setEnabled(False)
356
+ self.useCUDASOAIntegrator.setChecked(False)
357
+ else:
358
+ self.useCUDASOAIntegrator.setEnabled(True)
359
+ self.useCUDASOAIntegrator.setChecked(True)
360
+
361
+ def _initSingalsSlots(self):
362
+ """initialize (connect) signial and slots for geometric advanced settings
363
+ """
364
+
365
+ self.nonStandardSolventPsfButton.clicked.connect(
366
+ commonSlots.openFileDialog(
367
+ 'psf/parm/top',
368
+ self.nonStandardSolventPsfLineEdit
369
+ )
370
+ )
371
+ self.nonStandardSolventPdbButton.clicked.connect(
372
+ commonSlots.openFileDialog(
373
+ 'pdb/gro',
374
+ self.nonStandardSolventPdbLineEdit
375
+ )
376
+ )
377
+ self.geometricAdvancedSettingsOKButton.clicked.connect(self.close)
378
+ self.useGaWTMCheckbox.toggled.connect(self._toggleGaWTMBox)
379
+
380
+
381
+ class alchemicalAdvancedSettings(QWidget):
382
+ """advanced settings for the alchemical route
383
+ set the number of stratification windows
384
+ """
385
+
386
+ def __init__(self):
387
+ super().__init__()
388
+ self._initUI()
389
+ self._initSingalsSlots()
390
+ self.setWindowTitle('Alchemical advanced settings')
391
+ self.setGeometry(0,0,0,0)
392
+ #self.show()
393
+
394
+ def _initUI(self):
395
+ """initialize UI for the alchemical advanced settings
396
+ """
397
+
398
+ self.mainLayout = QVBoxLayout()
399
+
400
+ # stratification windows
401
+ self.stratification = QGroupBox('Stratification (number of strata)')
402
+ self.stratificationLayout = QGridLayout()
403
+
404
+ self.boundLigandLabel = QLabel('Ligand/Bound state:')
405
+ self.boundLigandLineEdit = QLineEdit('50')
406
+ self.unboundLigandLabel = QLabel('Ligand/Unbound state:')
407
+ self.unboundLigandLineEdit = QLineEdit('20')
408
+ self.boundRestraintsLabel = QLabel('Restraints/Bound state:')
409
+ self.boundRestraintsLineEdit = QLineEdit('50')
410
+ self.unboundRestraintsLabel = QLabel('Restraints/Unbound state:')
411
+ self.unboundRestraintsLineEdit = QLineEdit('20')
412
+
413
+ self.stratificationLayout.addWidget(self.boundLigandLabel, 0, 0)
414
+ self.stratificationLayout.addWidget(self.boundLigandLineEdit, 0, 1)
415
+ self.stratificationLayout.addWidget(self.unboundLigandLabel, 0, 2)
416
+ self.stratificationLayout.addWidget(self.unboundLigandLineEdit, 0, 3)
417
+ self.stratificationLayout.addWidget(self.boundRestraintsLabel, 1, 0)
418
+ self.stratificationLayout.addWidget(self.boundRestraintsLineEdit, 1, 1)
419
+ self.stratificationLayout.addWidget(self.unboundRestraintsLabel, 1, 2)
420
+ self.stratificationLayout.addWidget(self.unboundRestraintsLineEdit, 1, 3)
421
+
422
+ self.stratification.setLayout(self.stratificationLayout)
423
+
424
+ # double-wide simulation
425
+ self.doubleWide = QGroupBox('Double-wide sampling simulation')
426
+ self.doubleWideLayout = QGridLayout()
427
+
428
+ self.doubleWideCheckbox = QCheckBox('Generate input files for double-wide sampling')
429
+ self.doubleWideCheckbox.setChecked(True)
430
+ self.doubleWideLayout.addWidget(self.doubleWideCheckbox)
431
+ self.doubleWide.setLayout(self.doubleWideLayout)
432
+
433
+ # minimize before sampling in each window
434
+ self.minBeforeSample = QGroupBox('Minimization before sampling')
435
+ self.minBeforeSampleLayout = QVBoxLayout()
436
+
437
+ self.minBeforeSampleCheckbox = QCheckBox('Minimize before sampling in each window')
438
+ self.minBeforeSampleCheckbox.setChecked(False)
439
+ self.minBeforeSampleLayout.addWidget(self.minBeforeSampleCheckbox)
440
+ self.minBeforeSample.setLayout(self.minBeforeSampleLayout)
441
+
442
+ # compatibility
443
+ self.compatibility = QGroupBox('Compatibility')
444
+ self.compatibilityLayout = QGridLayout()
445
+
446
+ self.pinDownProCheckbox = QCheckBox('Pinning down the protein')
447
+ self.pinDownProCheckbox.setChecked(True)
448
+
449
+ self.useOldCvCheckbox = QCheckBox('Use quaternion-based CVs')
450
+ self.useOldCvCheckbox.setChecked(False)
451
+
452
+ self.useCUDASOAIntegrator = QCheckBox('Use CUDASOA integrator')
453
+ self.useCUDASOAIntegrator.setChecked(True)
454
+
455
+ self.compatibilityLayout.addWidget(self.pinDownProCheckbox, 0, 0)
456
+ self.compatibilityLayout.addWidget(self.useOldCvCheckbox, 0, 1)
457
+ self.compatibilityLayout.addWidget(self.useCUDASOAIntegrator, 1, 0)
458
+ self.compatibility.setLayout(self.compatibilityLayout)
459
+
460
+ # force field settings
461
+ self.FFSettings = QGroupBox('Force field settings')
462
+ self.FFSettingsLayout = QHBoxLayout()
463
+
464
+ self.OPLSMixingRuleCheckbox = QCheckBox('OPLS mixing rules')
465
+ self.OPLSMixingRuleCheckbox.setChecked(False)
466
+
467
+ self.timestepLayout = QHBoxLayout()
468
+ self.timestepLabel = QLabel(' Timestep:')
469
+ self.timestepLineEdit = QLineEdit('2.0')
470
+ self.timestepLayout.addWidget(self.timestepLabel)
471
+ self.timestepLayout.addWidget(self.timestepLineEdit)
472
+
473
+ self.FFSettingsLayout.addWidget(self.OPLSMixingRuleCheckbox)
474
+ self.FFSettingsLayout.addLayout(self.timestepLayout)
475
+ self.FFSettings.setLayout(self.FFSettingsLayout)
476
+
477
+ # strategy settings
478
+ self.strategy = QGroupBox('Strategy settings')
479
+ self.strategyLayout = QGridLayout()
480
+
481
+ self.considerRMSDCVCheckbox = QCheckBox('Take into account RMSD CV')
482
+ self.considerRMSDCVCheckbox.setChecked(True)
483
+
484
+ self.reEqCheckbox = QCheckBox('Re-equilibration after histogram')
485
+ self.reEqCheckbox.setChecked(True)
486
+
487
+ self.LDDMCheckbox = QCheckBox('Use LDDM strategy')
488
+ self.LDDMCheckbox.setChecked(False)
489
+
490
+ self.useWTMLambdaABFCheckbox = QCheckBox('Use WTM-lambdaABF')
491
+ self.useWTMLambdaABFCheckbox.setChecked(False)
492
+
493
+ self.strategyLayout.addWidget(self.considerRMSDCVCheckbox, 0, 0)
494
+ self.strategyLayout.addWidget(self.reEqCheckbox, 0, 1)
495
+ self.strategyLayout.addWidget(self.LDDMCheckbox, 1, 0)
496
+ self.strategyLayout.addWidget(self.useWTMLambdaABFCheckbox, 1, 1)
497
+ self.strategy.setLayout(self.strategyLayout)
498
+
499
+ # membrane protein
500
+ self.modeling = QGroupBox('Modeling (avaiable for CHARMM FF)')
501
+ self.modelingLayout = QVBoxLayout()
502
+
503
+ self.memProCheckbox = QCheckBox('Membrane protein')
504
+ self.memProCheckbox.setChecked(False)
505
+
506
+ self.neutralizeLigOnlyLayout = QHBoxLayout()
507
+ self.neutralizeLigOnlyLabel = QLabel('Auto-neutralize ligand-only system by:')
508
+ self.neutralizeLigOnlyCombobox = QComboBox()
509
+ self.neutralizeLigOnlyCombobox.addItem('NaCl')
510
+ self.neutralizeLigOnlyCombobox.addItem('KCl')
511
+ self.neutralizeLigOnlyCombobox.addItem('CaCl2')
512
+ self.neutralizeLigOnlyCombobox.addItem('None')
513
+ self.neutralizeLigOnlyLayout.addWidget(self.neutralizeLigOnlyLabel)
514
+ self.neutralizeLigOnlyLayout.addWidget(self.neutralizeLigOnlyCombobox)
515
+
516
+ self.modelingLayout.addWidget(self.memProCheckbox)
517
+ self.modelingLayout.addLayout(self.neutralizeLigOnlyLayout)
518
+ self.modeling.setLayout(self.modelingLayout)
519
+
520
+ self.alchemicalAdvancedSettingsButtonLayout = QHBoxLayout()
521
+ self.alchemicalAdvancedSettingsOKButton = QPushButton('OK')
522
+ #self.alchemicalAdvancedSettingsCancelButton = QPushButton('Cancel')
523
+ self.alchemicalAdvancedSettingsButtonLayout.addWidget(QSplitter())
524
+ self.alchemicalAdvancedSettingsButtonLayout.addWidget(self.alchemicalAdvancedSettingsOKButton)
525
+ #self.alchemicalAdvancedSettingsButtonLayout.addWidget(self.alchemicalAdvancedSettingsCancelButton)
526
+
527
+ self.mainLayout.addWidget(self.stratification)
528
+ self.mainLayout.addWidget(self.doubleWide)
529
+ self.mainLayout.addWidget(self.compatibility)
530
+ self.mainLayout.addWidget(self.FFSettings)
531
+ self.mainLayout.addWidget(self.strategy)
532
+ self.mainLayout.addWidget(self.minBeforeSample)
533
+ self.mainLayout.addWidget(self.modeling)
534
+ self.mainLayout.addLayout(self.alchemicalAdvancedSettingsButtonLayout)
535
+ self.setLayout(self.mainLayout)
536
+
537
+ # below are slow functions
538
+ def _toggleLDDMBox(self, checked):
539
+ """when enable LDDM, restraint simulations and considering RMSD are not needed, and
540
+ re-equilbration and double-wide simulations are necessary.
541
+ """
542
+
543
+ if checked:
544
+ self.boundRestraintsLineEdit.setEnabled(False)
545
+ self.unboundRestraintsLineEdit.setEnabled(False)
546
+ self.considerRMSDCVCheckbox.setChecked(False)
547
+ self.considerRMSDCVCheckbox.setEnabled(False)
548
+ self.reEqCheckbox.setChecked(True)
549
+ self.reEqCheckbox.setEnabled(False)
550
+ self.doubleWideCheckbox.setChecked(True)
551
+ self.doubleWideCheckbox.setEnabled(False)
552
+ self.minBeforeSampleCheckbox.setChecked(False)
553
+ self.minBeforeSampleCheckbox.setEnabled(False)
554
+ self.useWTMLambdaABFCheckbox.setChecked(False)
555
+ self.useWTMLambdaABFCheckbox.setEnabled(False)
556
+ else:
557
+ self.boundRestraintsLineEdit.setEnabled(True)
558
+ self.unboundRestraintsLineEdit.setEnabled(True)
559
+ self.considerRMSDCVCheckbox.setEnabled(True)
560
+ self.reEqCheckbox.setEnabled(True)
561
+ self.doubleWideCheckbox.setEnabled(True)
562
+ self.minBeforeSampleCheckbox.setEnabled(True)
563
+ self.useWTMLambdaABFCheckbox.setEnabled(True)
564
+
565
+ def _initSingalsSlots(self):
566
+ """initialize (connect) signals and slots for the alchemical advanced settings
567
+ """
568
+
569
+ self.LDDMCheckbox.toggled.connect(self._toggleLDDMBox)
570
+ self.alchemicalAdvancedSettingsOKButton.clicked.connect(self.close)
571
+
572
+ class AIAssistantDialog(QWidget):
573
+ """AI Assistant dialog for interactive help with OpenRouter API"""
574
+
575
+ def __init__(self, parent=None):
576
+ super().__init__(parent)
577
+ self.parent = parent
578
+ self._initUI()
579
+ self._initSignalsSlots()
580
+ self.setWindowTitle('AI Assistant')
581
+ self.setGeometry(100, 100, 800, 600)
582
+ self.setWindowFlags(self.windowFlags() | QtCore.Qt.Dialog | QtCore.Qt.Tool)
583
+ self.messageHistory = ""
584
+
585
+ def _initUI(self):
586
+ """Initialize the UI for AI Assistant dialog"""
587
+
588
+ self.mainLayout = QVBoxLayout()
589
+
590
+ # Conversation display area
591
+ self.conversationDisplay = QListWidget()
592
+ self.conversationDisplay.setWordWrap(True)
593
+ self.conversationDisplay.setAlternatingRowColors(True)
594
+
595
+ # Input area
596
+ self.inputLayout = QHBoxLayout()
597
+ self.inputLineEdit = QLineEdit()
598
+ self.inputLineEdit.setPlaceholderText("Type your message here...")
599
+ self.sendButton = QPushButton('Send')
600
+
601
+ self.inputLayout.addWidget(self.inputLineEdit)
602
+ self.inputLayout.addWidget(self.sendButton)
603
+
604
+ # Clear conversation button
605
+ self.clearButton = QPushButton('Clear Conversation')
606
+
607
+ # Layout assembly
608
+ self.mainLayout.addWidget(QLabel('AI Assistant - Ask about and automate BFEE settings:'))
609
+ self.mainLayout.addWidget(self.conversationDisplay)
610
+ self.mainLayout.addLayout(self.inputLayout)
611
+ self.mainLayout.addWidget(self.clearButton)
612
+
613
+ self.setLayout(self.mainLayout)
614
+
615
+ def _initSignalsSlots(self):
616
+ """Initialize signals and slots"""
617
+ self.sendButton.clicked.connect(self._sendMessage)
618
+ self.inputLineEdit.returnPressed.connect(self._sendMessage)
619
+ self.clearButton.clicked.connect(self._clearMessageHistory)
620
+
621
+ def _sendMessage(self):
622
+ """Send message to OpenRouter API and display response"""
623
+
624
+ user_message = self.inputLineEdit.text().strip()
625
+ if not user_message:
626
+ return
627
+
628
+ # Add user message to display
629
+ self.conversationDisplay.addItem(f"You: {user_message}")
630
+ self.messageHistory += f"You: {user_message}\n"
631
+ self.inputLineEdit.clear()
632
+
633
+ # Get API settings from parent's mainSettings
634
+ if self.parent:
635
+ api_key = self.parent.mainSettings.openRouterAPILineEdit.text()
636
+ model = self.parent.mainSettings.openRouterModelLineEdit.text()
637
+ # optional parameters
638
+ temperature_text = self.parent.mainSettings.openRouterTemperatureLineEdit.text().strip()
639
+ top_p_text = self.parent.mainSettings.openRouterTopPLineEdit.text().strip()
640
+ else:
641
+ self.conversationDisplay.addItem("AI: Error - Cannot access API settings")
642
+ self.messageHistory += f"AI: Error - Cannot access API settings\n"
643
+ return
644
+
645
+ if not api_key or not model:
646
+ self.conversationDisplay.addItem("AI: Please configure OpenRouter API and model in Settings first")
647
+ self.messageHistory += f"AI: Please configure OpenRouter API and model in Settings first\n"
648
+ return
649
+
650
+ # Show a modal waiting dialog in front of the AI window
651
+ wait_dlg = QProgressDialog("Receiving server response...", None, 0, 0, self)
652
+ wait_dlg.setWindowTitle("Please wait")
653
+ wait_dlg.setCancelButton(None) # non-interactive
654
+ wait_dlg.setWindowModality(QtCore.Qt.WindowModal) # block AI window
655
+ wait_dlg.setAutoClose(False)
656
+ wait_dlg.setAutoReset(False)
657
+ wait_dlg.setMinimumDuration(0)
658
+
659
+ # Remove close/help buttons
660
+ wait_dlg.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False)
661
+ wait_dlg.setWindowFlag(QtCore.Qt.WindowContextHelpButtonHint, False)
662
+ wait_dlg.show()
663
+ QApplication.processEvents()
664
+
665
+ # Prepare API request
666
+ try:
667
+ headers = {
668
+ "Authorization": f"Bearer {api_key}",
669
+ "Content-Type": "application/json"
670
+ }
671
+
672
+ data = {
673
+ "model": model,
674
+ "messages": [
675
+ {"role": "system", "content": rags.systemPrompt},
676
+ {"role": "user", "content": self.messageHistory}
677
+ ]
678
+ }
679
+ if temperature_text:
680
+ try:
681
+ data["temperature"] = float(temperature_text)
682
+ except ValueError:
683
+ pass
684
+ if top_p_text:
685
+ try:
686
+ data["top_p"] = float(top_p_text)
687
+ except ValueError:
688
+ pass
689
+
690
+ response = requests.post(
691
+ "https://openrouter.ai/api/v1/chat/completions",
692
+ headers=headers,
693
+ json=data,
694
+ timeout=30
695
+ )
696
+
697
+ if response.status_code == 200:
698
+ result = response.json()
699
+ ai_response = result['choices'][0]['message']['content']
700
+ self.conversationDisplay.addItem(f"AI: {ai_response}")
701
+ self.messageHistory += f"AI: {ai_response}\n"
702
+ self._execute_suggested_calls(ai_response)
703
+ else:
704
+ self.conversationDisplay.addItem(f"AI: Error - API request failed: {response.status_code}")
705
+ self.messageHistory += f"AI: Error - API request failed: {response.status_code}\n"
706
+
707
+ except Exception as e:
708
+ self.conversationDisplay.addItem(f"AI: Error - {str(e)}")
709
+ self.messageHistory += f"AI: Error - {str(e)}\n"
710
+ finally:
711
+ wait_dlg.close()
712
+
713
+ def _clearMessageHistory(self):
714
+ """Clear the message history"""
715
+ self.conversationDisplay.clear()
716
+ self.messageHistory = ""
717
+
718
+ def _execute_suggested_calls(self, ai_text: str):
719
+ """
720
+ Finds and executes function calls specified in the AI's output.
721
+ """
722
+ # 1. marker sentence
723
+ start_marker = "I will call the following functions:"
724
+
725
+ # 2. find the marker in output
726
+ try:
727
+ # do not distinguish upper and lower letters
728
+ search_text = ai_text.casefold()
729
+ marker_pos = search_text.find(start_marker.casefold())
730
+
731
+ if marker_pos == -1:
732
+ return
733
+ except AttributeError:
734
+ return
735
+ # 3. find '['
736
+ open_bracket_pos = ai_text.find('[', marker_pos)
737
+ if open_bracket_pos == -1:
738
+ return
739
+ # 4. find ']'
740
+ close_bracket_pos = ai_text.find(']', open_bracket_pos)
741
+ if close_bracket_pos == -1:
742
+ return
743
+ # 5. get things between "[]"
744
+ block = ai_text[open_bracket_pos + 1 : close_bracket_pos].strip()
745
+
746
+ if not block:
747
+ return
748
+
749
+ calls = self._split_top_level_commas(block)
750
+ for call in calls:
751
+ call = call.strip()
752
+ if not call:
753
+ continue
754
+ m2 = re.match(
755
+ r'^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\s*\((.*)\)\s*$',
756
+ call
757
+ )
758
+ if not m2:
759
+ msg = f"Exec: skip unsupported expression: {call}"
760
+ self.conversationDisplay.addItem(msg)
761
+ self.messageHistory += msg + "\n"
762
+ continue
763
+ path = m2.group(1)
764
+ args_str = m2.group(2).strip()
765
+ try:
766
+ # assume it is of self.parent
767
+ target = self.parent
768
+ for part in path.split('.'):
769
+ target = getattr(target, part)
770
+ # args
771
+ args = self._parse_args(args_str)
772
+ # execute
773
+ result = target(*args)
774
+ # report
775
+ args_repr = ", ".join(repr(a) for a in args)
776
+ msg = f"Exec: {path}({args_repr}) -> OK"
777
+ self.conversationDisplay.addItem(msg)
778
+ self.messageHistory += msg + "\n"
779
+ except Exception as e:
780
+ msg = f"Exec: {path} failed: {e}"
781
+ self.conversationDisplay.addItem(msg)
782
+ self.messageHistory += msg + "\n"
783
+
784
+ def _split_top_level_commas(self, s: str):
785
+ """split using comma"""
786
+ parts = []
787
+ cur = []
788
+ level = 0
789
+ in_str = None
790
+ escape = False
791
+ for ch in s:
792
+ if in_str:
793
+ cur.append(ch)
794
+ if escape:
795
+ escape = False
796
+ elif ch == '\\':
797
+ escape = True
798
+ elif ch == in_str:
799
+ in_str = None
800
+ continue
801
+ if ch in ("'", '"'):
802
+ in_str = ch
803
+ cur.append(ch)
804
+ continue
805
+ if ch == '(':
806
+ level += 1
807
+ cur.append(ch)
808
+ elif ch == ')':
809
+ level = max(0, level - 1)
810
+ cur.append(ch)
811
+ elif ch == ',' and level == 0:
812
+ part = ''.join(cur).strip()
813
+ if part:
814
+ parts.append(part)
815
+ cur = []
816
+ else:
817
+ cur.append(ch)
818
+ if cur:
819
+ part = ''.join(cur).strip()
820
+ if part:
821
+ parts.append(part)
822
+ return parts
823
+
824
+ def _parse_args(self, s: str):
825
+ """understand the type of args in AI-output calls"""
826
+ if s == '':
827
+ return []
828
+ items = self._split_top_level_commas(s)
829
+ args = []
830
+ for it in items:
831
+ t = it.strip()
832
+ if t == '':
833
+ continue
834
+ if t == 'True':
835
+ args.append(True)
836
+ elif t == 'False':
837
+ args.append(False)
838
+ elif t == 'None':
839
+ args.append(None)
840
+ elif (t.startswith("'") and t.endswith("'")) or (t.startswith('"') and t.endswith('"')):
841
+ args.append(t[1:-1])
842
+ else:
843
+ # try float or int
844
+ try:
845
+ if any(c in t for c in ('.', 'e', 'E')):
846
+ args.append(float(t))
847
+ else:
848
+ args.append(int(t, 0))
849
+ except Exception:
850
+ # common string
851
+ args.append(t)
852
+ return args
853
+
854
+ class mainUI(QMainWindow):
855
+ """the main window UI
856
+ include the preTreatment, postTreatment and QuickPlot tab
857
+ the preTreatment tab include NAMD and Gromacs tab
858
+ the postTreatment tab include geometric and alchemical tab
859
+ """
860
+
861
+ def __init__(self):
862
+ super().__init__()
863
+
864
+ self._initActions()
865
+
866
+ self._initNAMDTab()
867
+ self._initGromacsTab()
868
+ self._initPreTreatmentTab()
869
+ self._initQuickPlotTab()
870
+
871
+ self._initGeometricTab()
872
+ self._initAlchemicalTab()
873
+ self._initLDDMTab()
874
+ self._initPostTreatmentTab()
875
+
876
+ self._initSingalsSlots()
877
+
878
+ self._initMainUI()
879
+
880
+ # other dialogs
881
+ self.mainSettings = mainSettings()
882
+ self.alchemicalAdvancedSettings = alchemicalAdvancedSettings()
883
+ self.geometricAdvancedSettings = geometricAdvancedSettings()
884
+ self.aiAssistantDialog = AIAssistantDialog(self)
885
+ self.aiAssistantDialog.hide()
886
+
887
+ self.setGeometry(0,0,0,0)
888
+ self.setWindowTitle(__PROGRAM_NAME__)
889
+ self.setWindowIcon(QIcon("BFEE2/icon/icon.png"))
890
+ self.show()
891
+
892
+ self._showMDEngineVersionWarning()
893
+
894
+ def _showMDEngineVersionWarning(self):
895
+ ''' show a message box ask for the latest NAMD version '''
896
+ QMessageBox.warning(self,
897
+ 'Warning',
898
+ f'\
899
+ {__PROGRAM_NAME__} is fully compatible with NAMD {__NAMD_VERSION__} or GROMACS {__GMX_VERSION__}. \n\
900
+ Please use the same or a later version of NAMD or GROMACS if you have any problem.\n'
901
+ )
902
+
903
+ def _initActions(self):
904
+ ''' initialize actions for menubar '''
905
+
906
+ # settings
907
+ self.settingsAction = QAction('&Settings', self)
908
+ self.settingsAction.setStatusTip('Setting third-party service')
909
+ self.settingsAction.triggered.connect(self._mainSettings())
910
+
911
+ # exit
912
+ self.exitAction = QAction('&Exit', self)
913
+ self.exitAction.setStatusTip('Exit application')
914
+ self.exitAction.triggered.connect(QApplication.quit)
915
+
916
+ # quick settings
917
+ # geometrical protein protein
918
+ self.quickGeometricProteinProteinAction = QAction('Protein-Protein / Geometrical')
919
+ self.quickGeometricProteinProteinAction.setStatusTip(
920
+ 'Change settings for geometrical protein-protein binding free-energy calculations'
921
+ )
922
+ self.quickGeometricProteinProteinAction.triggered.connect(self._quickSetProteinProteinGeometric)
923
+
924
+ # geometrical protein ligand
925
+ self.quickGeometricProteinLigandAction = QAction('Protein-Ligand / Geometrical')
926
+ self.quickGeometricProteinLigandAction.setStatusTip(
927
+ 'Change settings for geometrical protein-ligand binding free-energy calculations'
928
+ )
929
+ self.quickGeometricProteinLigandAction.triggered.connect(self._quickSetProteinLigandGeometric)
930
+
931
+ # alchemical protein ligand
932
+ self.quickAlchemicalProteinLigandAction = QAction('Protein-Ligand / Alchemical')
933
+ self.quickAlchemicalProteinLigandAction.setStatusTip(
934
+ 'Change settings for alchemical protein-ligand binding free-energy calculations'
935
+ )
936
+ self.quickAlchemicalProteinLigandAction.triggered.connect(self._quickSetProteinLigandAlchemical)
937
+
938
+ # LDDM
939
+ self.quickLDDMProteinLigandAction = QAction('Protein-Ligand / LDDM')
940
+ self.quickLDDMProteinLigandAction.setStatusTip(
941
+ 'Change settings for protein-ligand binding free-energy calculations using the LDDM strategy'
942
+ )
943
+ self.quickLDDMProteinLigandAction.triggered.connect(self._quickSetProteinLigandLDDM)
944
+
945
+ # LDDM
946
+ self.quickAISettingAction = QAction('AI-Assisted Setting')
947
+ self.quickAISettingAction.setStatusTip(
948
+ 'Set up simulations using AI assistance'
949
+ )
950
+ self.quickAISettingAction.triggered.connect(self._quickSetAI)
951
+
952
+ # help
953
+ self.helpAction = QAction('&Help (Deprecated)', self)
954
+ self.helpAction.setStatusTip('Open user manual')
955
+ self.helpAction.triggered.connect(self._openDocFile)
956
+
957
+ # python API
958
+ self.pythonAPIAction = QAction('&Python API', self)
959
+ self.pythonAPIAction.setStatusTip('Open Python API Documentation')
960
+ self.pythonAPIAction.triggered.connect(self._openPythonAPIFile)
961
+
962
+ # about
963
+ self.aboutAction = QAction('&About', self)
964
+ self.aboutAction.setStatusTip('About BFEEstimator')
965
+ self.aboutAction.triggered.connect(self._showAboutBox())
966
+
967
+
968
+ def _initMainUI(self):
969
+ """initialize main window
970
+ """
971
+
972
+ # status bar
973
+ #self.statusBar()
974
+
975
+ # menu bar
976
+ menubar = self.menuBar()
977
+ menubar.setNativeMenuBar(False)
978
+ self.fileMenu = menubar.addMenu('&File')
979
+ self.fileMenu.addAction(self.settingsAction)
980
+ self.fileMenu.addSeparator()
981
+ self.fileMenu.addAction(self.exitAction)
982
+
983
+ self.quickSettingsMenu = menubar.addMenu('&Quick Settings')
984
+ self.quickSettingsMenu.addAction(self.quickGeometricProteinProteinAction)
985
+ self.quickSettingsMenu.addAction(self.quickGeometricProteinLigandAction)
986
+ self.quickSettingsMenu.addAction(self.quickAlchemicalProteinLigandAction)
987
+ self.quickSettingsMenu.addAction(self.quickLDDMProteinLigandAction)
988
+ self.quickSettingsMenu.addAction(self.quickAISettingAction)
989
+
990
+ self.helpMenu = menubar.addMenu('&Help')
991
+ self.helpMenu.addAction(self.helpAction)
992
+ self.helpMenu.addAction(self.pythonAPIAction)
993
+ self.helpMenu.addSeparator()
994
+ self.helpMenu.addAction(self.aboutAction)
995
+
996
+ # main layout
997
+ self.mainLayout = QVBoxLayout()
998
+
999
+ # title
1000
+ self.title = QLabel('Binding Free Energy Estimator')
1001
+ titleFont = QFont()
1002
+ titleFont.setBold(True)
1003
+ self.title.setFont(titleFont)
1004
+ self.titleBox = QGroupBox()
1005
+ self.titleBoxLayout = QVBoxLayout()
1006
+ self.titleBoxLayout.addWidget(self.title, alignment=QtCore.Qt.AlignCenter)
1007
+ self.titleBox.setLayout(self.titleBoxLayout)
1008
+
1009
+ # tabs
1010
+ self.mainTabs = QTabWidget()
1011
+ #self.preTreatmentTab = QWidget()
1012
+ #self.postTreatmentTab = QWidget()
1013
+ #self.quickPlot = QWidget()
1014
+
1015
+ self.mainTabs.addTab(self.preTreatmentTab, 'Pre-treatment')
1016
+ self.mainTabs.addTab(self.postTreatmentTab, 'Post-treatment')
1017
+ self.mainTabs.addTab(self.quickPlot, 'Quick-Plot')
1018
+
1019
+ # main layout
1020
+ #self.mainLayout.addWidget(self.titleBox)
1021
+ self.mainLayout.addWidget(self.mainTabs)
1022
+ self.mainWidgit = QWidget()
1023
+ self.mainWidgit.setLayout(self.mainLayout)
1024
+ self.setCentralWidget(self.mainWidgit)
1025
+
1026
+ def _initPreTreatmentTab(self):
1027
+ """initialize pre-treatment tab
1028
+ """
1029
+
1030
+ self.preTreatmentTab = QWidget()
1031
+
1032
+ # pre-treatment tabs
1033
+ # NAMD and gromacs
1034
+ self.preTreatmentMainTabs = QTabWidget()
1035
+
1036
+ self.preTreatmentMainTabs.addTab(self.NAMDTab, 'NAMD/Gromacs(CHARMM/Amber files)')
1037
+ self.preTreatmentMainTabs.addTab(self.GromacsTab, 'Gromacs(Gromacs files)')
1038
+
1039
+ self.preTreatmentMainLayout = QVBoxLayout()
1040
+ self.preTreatmentMainLayout.addWidget(self.preTreatmentMainTabs)
1041
+
1042
+ # other parameters
1043
+ self.otherParameters = QGroupBox('Other parameters')
1044
+ self.otherParametersLayout = QVBoxLayout()
1045
+
1046
+ # temperature, selection protein and ligand layout
1047
+ self.otherParametersChildLayout = QGridLayout()
1048
+
1049
+ # temperature
1050
+ self.temperatureLabel = QLabel('Temperature: ')
1051
+ self.temperatureLineEdit = QLineEdit('300')
1052
+ self.otherParametersChildLayout.addWidget(self.temperatureLabel, 0, 0)
1053
+ self.otherParametersChildLayout.addWidget(self.temperatureLineEdit, 0, 1)
1054
+
1055
+ # select protein
1056
+ self.selectProteinLabel = QLabel('Select protein: ')
1057
+ self.selectProteineLineEdit = QLineEdit('segid SH3D')
1058
+ self.otherParametersChildLayout.addWidget(self.selectProteinLabel, 1, 0)
1059
+ self.otherParametersChildLayout.addWidget(self.selectProteineLineEdit, 1, 1)
1060
+
1061
+ # select ligand
1062
+ self.selectLigandLabel = QLabel('Select ligand: ')
1063
+ self.selectLigandLineEdit = QLineEdit('segid PPRO')
1064
+ self.otherParametersChildLayout.addWidget(self.selectLigandLabel, 2, 0)
1065
+ self.otherParametersChildLayout.addWidget(self.selectLigandLineEdit, 2, 1)
1066
+
1067
+ # select strategy
1068
+ self.selectStrategyLayout = QHBoxLayout()
1069
+
1070
+ self.selectStrategyLabel = QLabel('Select MD engine and strategy: ')
1071
+ self.selectStrategyCombobox = QComboBox()
1072
+ self.selectStrategyCombobox.addItem('Geometric')
1073
+ self.selectStrategyCombobox.addItem('Alchemical')
1074
+ self.selectStrategyAdvancedButton = QPushButton('Advanced settings')
1075
+
1076
+ self.selectMDEngineCombobox = QComboBox()
1077
+ self.selectMDEngineCombobox.addItem('NAMD')
1078
+ self.selectMDEngineCombobox.addItem('Gromacs')
1079
+
1080
+ self.selectStrategyChildLayout = QHBoxLayout()
1081
+ self.selectStrategyChildLayout.addWidget(self.selectMDEngineCombobox)
1082
+ self.selectStrategyChildLayout.addWidget(self.selectStrategyCombobox)
1083
+ self.selectStrategyChildLayout.addWidget(self.selectStrategyAdvancedButton)
1084
+
1085
+ self.selectStrategyLayout.addWidget(self.selectStrategyLabel)
1086
+ self.selectStrategyLayout.addLayout(self.selectStrategyChildLayout)
1087
+
1088
+
1089
+ # generate input button
1090
+ self.generateInputButton = QPushButton('Generate Inputs')
1091
+
1092
+ self.otherParametersLayout.addLayout(self.otherParametersChildLayout)
1093
+ self.otherParametersLayout.addLayout(self.selectStrategyLayout)
1094
+ self.otherParameters.setLayout(self.otherParametersLayout)
1095
+
1096
+ self.preTreatmentMainLayout.addWidget(self.otherParameters)
1097
+ self.preTreatmentMainLayout.addWidget(self.generateInputButton)
1098
+
1099
+ self.preTreatmentTab.setLayout(self.preTreatmentMainLayout)
1100
+
1101
+ def _initNAMDTab(self):
1102
+ """initialize NAMD Tab in pre-treatment Tab
1103
+ """
1104
+
1105
+ self.NAMDTab = QWidget()
1106
+ self.NAMDTabMainLayout = QVBoxLayout()
1107
+
1108
+ # inputs for the complex
1109
+ self.inputsForComplex = QGroupBox('Inputs for complex')
1110
+ self.inputsForComplexLayout = QGridLayout()
1111
+
1112
+ # psf/parm
1113
+ self.psfLabel = QLabel('psf/parm file:')
1114
+ self.psfLineEdit = QLineEdit()
1115
+ self.psfButton = QPushButton('Browse')
1116
+ self.inputsForComplexLayout.addWidget(self.psfLabel, 0, 0)
1117
+ self.inputsForComplexLayout.addWidget(self.psfLineEdit, 0, 1)
1118
+ self.inputsForComplexLayout.addWidget(self.psfButton, 0, 2)
1119
+
1120
+ # coor
1121
+ self.coorLabel = QLabel('pdb/rst file:')
1122
+ self.coorLineEdit = QLineEdit()
1123
+ self.coorButton = QPushButton('Browse')
1124
+ self.inputsForComplexLayout.addWidget(self.coorLabel, 1, 0)
1125
+ self.inputsForComplexLayout.addWidget(self.coorLineEdit, 1, 1)
1126
+ self.inputsForComplexLayout.addWidget(self.coorButton, 1, 2)
1127
+
1128
+ # force fields
1129
+ self.forceFields = QGroupBox('Force fields')
1130
+ self.forceFieldsLayout = QVBoxLayout()
1131
+
1132
+ # force field type
1133
+ self.forceFieldTypeLayout = QHBoxLayout()
1134
+ self.forceFieldTypeLabel = QLabel('Force field type:')
1135
+ self.forceFieldCombobox = QComboBox()
1136
+ self.forceFieldCombobox.addItem('CHARMM')
1137
+ self.forceFieldCombobox.addItem('Amber')
1138
+
1139
+ self.forceFieldTypeLayout.addWidget(self.forceFieldTypeLabel)
1140
+ self.forceFieldTypeLayout.addWidget(self.forceFieldCombobox)
1141
+
1142
+ # CHARMM force field files
1143
+ self.forceFieldFilesLayout = QVBoxLayout()
1144
+ self.forceFieldFilesLabel = QLabel('Force field files:')
1145
+ self.forceFieldFilesBox = QListWidget()
1146
+ self.forceFieldFilesChildLayout = QHBoxLayout()
1147
+ self.forceFieldAddButton = QPushButton('Add')
1148
+ self.forceFieldClearButton = QPushButton('Clear')
1149
+ self.forceFieldFilesChildLayout.addWidget(self.forceFieldAddButton)
1150
+ self.forceFieldFilesChildLayout.addWidget(self.forceFieldClearButton)
1151
+ self.forceFieldFilesLayout.addWidget(self.forceFieldFilesLabel)
1152
+ self.forceFieldFilesLayout.addWidget(self.forceFieldFilesBox)
1153
+ self.forceFieldFilesLayout.addLayout(self.forceFieldFilesChildLayout)
1154
+
1155
+ self.forceFieldsLayout.addLayout(self.forceFieldTypeLayout)
1156
+ self.forceFieldsLayout.addLayout(self.forceFieldFilesLayout)
1157
+
1158
+ self.inputsForComplex.setLayout(self.inputsForComplexLayout)
1159
+ self.forceFields.setLayout(self.forceFieldsLayout)
1160
+ self.NAMDTabMainLayout.addWidget(self.inputsForComplex)
1161
+ self.NAMDTabMainLayout.addWidget(self.forceFields)
1162
+ self.NAMDTab.setLayout(self.NAMDTabMainLayout)
1163
+
1164
+ def _initGromacsTab(self):
1165
+ """initialize GMX Tab in pre-treatment Tab
1166
+ """
1167
+
1168
+ self.GromacsTab = QWidget()
1169
+ self.GromacsTabMainLayout = QVBoxLayout()
1170
+
1171
+ # inputs for the complex
1172
+ self.GromacsInputsForComplex = QGroupBox('Inputs for complex')
1173
+ self.GromacsInputsForComplexLayout = QGridLayout()
1174
+
1175
+ # top
1176
+ self.topLabel = QLabel('Topology file: ')
1177
+ self.topLineEdit = QLineEdit()
1178
+ self.topButton = QPushButton('Browse')
1179
+ self.GromacsInputsForComplexLayout.addWidget(self.topLabel, 0, 0)
1180
+ self.GromacsInputsForComplexLayout.addWidget(self.topLineEdit, 0, 1)
1181
+ self.GromacsInputsForComplexLayout.addWidget(self.topButton, 0, 2)
1182
+
1183
+ # gro
1184
+ self.gromacsPdbLabel = QLabel('Structure file: ')
1185
+ self.gromacsPdbLineEdit = QLineEdit()
1186
+ self.gromacsPdbButton = QPushButton('Browse')
1187
+ self.GromacsInputsForComplexLayout.addWidget(self.gromacsPdbLabel, 1, 0)
1188
+ self.GromacsInputsForComplexLayout.addWidget(self.gromacsPdbLineEdit, 1, 1)
1189
+ self.GromacsInputsForComplexLayout.addWidget(self.gromacsPdbButton, 1, 2)
1190
+
1191
+ # structure file format
1192
+ self.gromacsStructureFileFormatLayout = QHBoxLayout()
1193
+ self.gromacsStructureFileFormatLabel = QLabel('Structure file format:')
1194
+ self.gromacsStructureFileFormatCombobox = QComboBox()
1195
+ self.gromacsStructureFileFormatCombobox.addItem('pdb')
1196
+ self.gromacsStructureFileFormatCombobox.addItem('xpdb')
1197
+ self.gromacsStructureFileFormatCombobox.setToolTip('Select "<b>xpdb</b>" if your PDB file has more than 9,999 number of residues')
1198
+ self.gromacsStructureFileFormatLayout.addWidget(self.gromacsStructureFileFormatLabel)
1199
+ self.gromacsStructureFileFormatLayout.addWidget(self.gromacsStructureFileFormatCombobox)
1200
+ self.GromacsInputsForComplexLayout.addLayout(self.gromacsStructureFileFormatLayout, 2, 0, 1, 3)
1201
+
1202
+ self.GromacsInputsForComplexLayout.addWidget(QSplitter(), 3, 1)
1203
+ self.GromacsInputsForComplex.setLayout(self.GromacsInputsForComplexLayout)
1204
+
1205
+ # inputs for the ligand-only system
1206
+ self.GromacsInputsLigandOnly = QGroupBox('Inputs for ligand-only system')
1207
+ self.GromacsInputsLigandOnlyLayout = QGridLayout()
1208
+
1209
+ # top
1210
+ self.gromacsLigandOnlyTopLabel = QLabel('Topology file: ')
1211
+ self.gromacsLigandOnlyTopLineEdit = QLineEdit()
1212
+ self.gromacsLigandOnlyTopButton = QPushButton('Browse')
1213
+ self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyTopLabel, 0, 0)
1214
+ self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyTopLineEdit, 0, 1)
1215
+ self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyTopButton, 0, 2)
1216
+
1217
+ # gro
1218
+ self.gromacsLigandOnlyPdbLabel = QLabel('Structure file: ')
1219
+ self.gromacsLigandOnlyPdbLineEdit = QLineEdit()
1220
+ self.gromacsLigandOnlyPdbButton = QPushButton('Browse')
1221
+ self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyPdbLabel, 1, 0)
1222
+ self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyPdbLineEdit, 1, 1)
1223
+ self.GromacsInputsLigandOnlyLayout.addWidget(self.gromacsLigandOnlyPdbButton, 1, 2)
1224
+
1225
+ # structure file format
1226
+ self.gromacsLigandOnlyStructureFileFormatLayout = QHBoxLayout()
1227
+ self.gromacsLigandOnlyStructureFileFormatLabel = QLabel('Structure file format:')
1228
+ self.gromacsLigandOnlyStructureFileFormatCombobox = QComboBox()
1229
+ self.gromacsLigandOnlyStructureFileFormatCombobox.addItem('pdb')
1230
+ self.gromacsLigandOnlyStructureFileFormatCombobox.addItem('xpdb')
1231
+ self.gromacsLigandOnlyStructureFileFormatCombobox.setToolTip('Select "<b>xpdb</b>" if your PDB file has more than 9,999 number of residues')
1232
+ self.gromacsLigandOnlyStructureFileFormatLayout.addWidget(self.gromacsLigandOnlyStructureFileFormatLabel)
1233
+ self.gromacsLigandOnlyStructureFileFormatLayout.addWidget(self.gromacsLigandOnlyStructureFileFormatCombobox)
1234
+ self.GromacsInputsLigandOnlyLayout.addLayout(self.gromacsLigandOnlyStructureFileFormatLayout, 2, 0, 1, 3)
1235
+
1236
+ self.GromacsInputsLigandOnlyLayout.addWidget(QSplitter(), 3, 1)
1237
+ self.GromacsInputsLigandOnly.setLayout(self.GromacsInputsLigandOnlyLayout)
1238
+
1239
+ self.GromacsTabMainLayout.addWidget(self.GromacsInputsForComplex)
1240
+ self.GromacsTabMainLayout.addWidget(self.GromacsInputsLigandOnly)
1241
+
1242
+ self.GromacsTab.setLayout(self.GromacsTabMainLayout)
1243
+
1244
+ def _initPostTreatmentTab(self):
1245
+ """initialize pre-treatment tab
1246
+ """
1247
+
1248
+ self.postTreatmentTab = QWidget()
1249
+
1250
+ # post-treatment tabs
1251
+ # Geometric and alchemical
1252
+ self.postTreatmentMainTabs = QTabWidget()
1253
+
1254
+ self.postTreatmentMainTabs.addTab(self.geometricTab, 'Geometric')
1255
+ self.postTreatmentMainTabs.addTab(self.alchemicalTab, 'Alchemical')
1256
+ self.postTreatmentMainTabs.addTab(self.LDDMTab, 'LDDM')
1257
+
1258
+ self.postTreatmentMainLayout = QVBoxLayout()
1259
+ self.postTreatmentMainLayout.addWidget(self.postTreatmentMainTabs)
1260
+
1261
+ self.calculateButton = QPushButton('Calculate binding free energy')
1262
+ self.postTreatmentMainLayout.addWidget(self.calculateButton)
1263
+
1264
+ self.postTreatmentTab.setLayout(self.postTreatmentMainLayout)
1265
+
1266
+ def _initGeometricTab(self):
1267
+ """initialize geometric tab of post-treatment
1268
+ """
1269
+
1270
+ self.geometricTab = QWidget()
1271
+ self.geometricTabLayout = QVBoxLayout()
1272
+
1273
+ # pmf inputs
1274
+ self.pmfInputs = QGroupBox('PMF inputs (.czar.pmf/.UI.pmf):')
1275
+ self.pmfInputsLayout = QVBoxLayout()
1276
+
1277
+ # bound stats
1278
+ self.boundStateLabel = QLabel('Bound state:')
1279
+ self.boundStateLayout = QGridLayout()
1280
+
1281
+ # RMSD
1282
+ self.rmsdBoundLabel = QLabel('RMSD: ')
1283
+ self.rmsdBoundLineEdit = QLineEdit()
1284
+ self.rmsdBoundButton = QPushButton('Browse')
1285
+ self.boundStateLayout.addWidget(self.rmsdBoundLabel, 0, 0)
1286
+ self.boundStateLayout.addWidget(self.rmsdBoundLineEdit, 0, 1)
1287
+ self.boundStateLayout.addWidget(self.rmsdBoundButton, 0, 2)
1288
+
1289
+ # Theta
1290
+ self.ThetaLabel = QLabel('Theta:')
1291
+ self.ThetaLineEdit = QLineEdit()
1292
+ self.ThetaButton = QPushButton('Browse')
1293
+ self.boundStateLayout.addWidget(self.ThetaLabel, 1, 0)
1294
+ self.boundStateLayout.addWidget(self.ThetaLineEdit, 1, 1)
1295
+ self.boundStateLayout.addWidget(self.ThetaButton, 1, 2)
1296
+
1297
+ # Phi
1298
+ self.PhiLabel = QLabel('Phi: ')
1299
+ self.PhiLineEdit = QLineEdit()
1300
+ self.PhiButton = QPushButton('Browse')
1301
+ self.boundStateLayout.addWidget(self.PhiLabel, 2, 0)
1302
+ self.boundStateLayout.addWidget(self.PhiLineEdit, 2, 1)
1303
+ self.boundStateLayout.addWidget(self.PhiButton, 2, 2)
1304
+
1305
+ # Psi
1306
+ self.PsiLabel = QLabel('Psi: ')
1307
+ self.PsiLineEdit = QLineEdit()
1308
+ self.PsiButton = QPushButton('Browse')
1309
+ self.boundStateLayout.addWidget(self.PsiLabel, 3, 0)
1310
+ self.boundStateLayout.addWidget(self.PsiLineEdit, 3, 1)
1311
+ self.boundStateLayout.addWidget(self.PsiButton, 3, 2)
1312
+
1313
+ # theta
1314
+ self.thetaLayout = QHBoxLayout()
1315
+ self.thetaLabel = QLabel('theta:')
1316
+ self.thetaLineEdit = QLineEdit()
1317
+ self.thetaButton = QPushButton('Browse')
1318
+ self.boundStateLayout.addWidget(self.thetaLabel, 4, 0)
1319
+ self.boundStateLayout.addWidget(self.thetaLineEdit, 4, 1)
1320
+ self.boundStateLayout.addWidget(self.thetaButton, 4, 2)
1321
+
1322
+ # phi
1323
+ self.phiLayout = QHBoxLayout()
1324
+ self.phiLabel = QLabel('phi: ')
1325
+ self.phiLineEdit = QLineEdit()
1326
+ self.phiButton = QPushButton('Browse')
1327
+ self.boundStateLayout.addWidget(self.phiLabel, 5, 0)
1328
+ self.boundStateLayout.addWidget(self.phiLineEdit, 5, 1)
1329
+ self.boundStateLayout.addWidget(self.phiButton, 5, 2)
1330
+
1331
+ # r
1332
+ self.rLabel = QLabel('r: ')
1333
+ self.rLineEdit = QLineEdit()
1334
+ self.rButton = QPushButton('Browse')
1335
+ self.boundStateLayout.addWidget(self.rLabel, 6, 0)
1336
+ self.boundStateLayout.addWidget(self.rLineEdit, 6, 1)
1337
+ self.boundStateLayout.addWidget(self.rButton, 6, 2)
1338
+
1339
+ self.unboundStateLabel = QLabel('Unbound state:')
1340
+
1341
+ # RMSD unbound
1342
+ self.rmsdUnboundLayout = QHBoxLayout()
1343
+ self.rmsdUnboundLabel = QLabel('RMSD: ')
1344
+ self.rmsdUnboundLineEdit = QLineEdit()
1345
+ self.rmsdUnboundButton = QPushButton('Browse')
1346
+ self.rmsdUnboundLayout.addWidget(self.rmsdUnboundLabel)
1347
+ self.rmsdUnboundLayout.addWidget(self.rmsdUnboundLineEdit)
1348
+ self.rmsdUnboundLayout.addWidget(self.rmsdUnboundButton)
1349
+
1350
+ self.pmfInputsLayout.addWidget(self.boundStateLabel)
1351
+ self.pmfInputsLayout.addLayout(self.boundStateLayout)
1352
+ self.pmfInputsLayout.addWidget(self.unboundStateLabel)
1353
+ self.pmfInputsLayout.addLayout(self.rmsdUnboundLayout)
1354
+
1355
+ self.pmfInputs.setLayout(self.pmfInputsLayout)
1356
+
1357
+ # force constants
1358
+ self.forceConstants = QGroupBox('Force constants (in Colvars unit):')
1359
+ self.forceConstantsLayout = QGridLayout()
1360
+
1361
+ self.fcBoundStateLabel = QLabel('Bound state:')
1362
+
1363
+ # all widgets
1364
+ self.fcRMSDLabel = QLabel('RMSD:')
1365
+ self.fcRMSDLineEdit = QLineEdit('10')
1366
+ self.fcThetaLabel = QLabel('Theta:')
1367
+ self.fcThetaLineEdit = QLineEdit('0.1')
1368
+ self.fcPhiLabel = QLabel('Phi:')
1369
+ self.fcPhiLineEdit = QLineEdit('0.1')
1370
+ self.fcPsiLabel = QLabel('Psi:')
1371
+ self.fcPsiLineEdit = QLineEdit('0.1')
1372
+ self.fcthetaLabel = QLabel('theta:')
1373
+ self.fcthetaLineEdit = QLineEdit('0.1')
1374
+ self.fcphiLabel = QLabel('phi:')
1375
+ self.fcphiLineEdit = QLineEdit('0.1')
1376
+
1377
+ self.forceConstantsLayout.addWidget(self.fcRMSDLabel, 0, 0)
1378
+ self.forceConstantsLayout.addWidget(self.fcRMSDLineEdit, 0, 1)
1379
+ self.forceConstantsLayout.addWidget(self.fcThetaLabel, 0, 2)
1380
+ self.forceConstantsLayout.addWidget(self.fcThetaLineEdit, 0, 3)
1381
+ self.forceConstantsLayout.addWidget(self.fcPhiLabel, 0, 4)
1382
+ self.forceConstantsLayout.addWidget(self.fcPhiLineEdit, 0, 5)
1383
+ self.forceConstantsLayout.addWidget(self.fcPsiLabel, 1, 0)
1384
+ self.forceConstantsLayout.addWidget(self.fcPsiLineEdit, 1, 1)
1385
+ self.forceConstantsLayout.addWidget(self.fcthetaLabel, 1, 2)
1386
+ self.forceConstantsLayout.addWidget(self.fcthetaLineEdit, 1, 3)
1387
+ self.forceConstantsLayout.addWidget(self.fcphiLabel, 1, 4)
1388
+ self.forceConstantsLayout.addWidget(self.fcphiLineEdit, 1, 5)
1389
+
1390
+ self.forceConstants.setLayout(self.forceConstantsLayout)
1391
+
1392
+ # other parameters
1393
+ self.postOtherParams = QGroupBox('Other parameters:')
1394
+ self.postOtherParamsLayout = QHBoxLayout()
1395
+
1396
+ self.postTemperatureLabel = QLabel('temperature:')
1397
+ self.postTemperatureLineEdit = QLineEdit('300')
1398
+ self.postRstarLabel = QLabel(' r*:')
1399
+ self.postRstarLineEdit = QLineEdit('30')
1400
+ self.postPMFTypeLabel = QLabel(' PMF type:')
1401
+ self.postPMFTypeBox = QComboBox()
1402
+ self.postPMFTypeBox.addItem('NAMD')
1403
+ self.postPMFTypeBox.addItem('Gromacs')
1404
+
1405
+ self.postOtherParamsLayout.addWidget(self.postTemperatureLabel)
1406
+ self.postOtherParamsLayout.addWidget(self.postTemperatureLineEdit)
1407
+ self.postOtherParamsLayout.addWidget(self.postRstarLabel)
1408
+ self.postOtherParamsLayout.addWidget(self.postRstarLineEdit)
1409
+ self.postOtherParamsLayout.addWidget(self.postPMFTypeLabel)
1410
+ self.postOtherParamsLayout.addWidget(self.postPMFTypeBox)
1411
+
1412
+ self.postOtherParams.setLayout(self.postOtherParamsLayout)
1413
+
1414
+ self.fcBoundStateLabel = QLabel('Bound state:')
1415
+
1416
+ self.geometricTabLayout.addWidget(self.pmfInputs)
1417
+ self.geometricTabLayout.addWidget(self.forceConstants)
1418
+ self.geometricTabLayout.addWidget(self.postOtherParams)
1419
+ self.geometricTab.setLayout(self.geometricTabLayout)
1420
+
1421
+ def _initAlchemicalTab(self):
1422
+ """initialize alchemical tab of post-treatment
1423
+ """
1424
+
1425
+ self.alchemicalTab = QWidget()
1426
+ self.alchemicalTabLayout = QVBoxLayout()
1427
+
1428
+
1429
+ self.restraintInputs = QGroupBox('Inputs for alchemical simulations (.fepout/.pmf/.log):')
1430
+ self.restraintInputsLayout = QVBoxLayout()
1431
+
1432
+ # bound state
1433
+ self.alchemicalBoundStateLabel = QLabel('Atoms/Bound state (.fepout/.pmf):')
1434
+ self.alchemicalBoundStateLayout = QGridLayout()
1435
+
1436
+ self.alchemicalForwardLabel1 = QLabel('Forward:')
1437
+ self.alchemicalForwardLineEdit1 = QLineEdit()
1438
+ self.alchemicalForwardButton1 = QPushButton('Browse')
1439
+ self.alchemicalBoundStateLayout.addWidget(self.alchemicalForwardLabel1, 0, 0)
1440
+ self.alchemicalBoundStateLayout.addWidget(self.alchemicalForwardLineEdit1, 0, 1)
1441
+ self.alchemicalBoundStateLayout.addWidget(self.alchemicalForwardButton1, 0, 2)
1442
+
1443
+ self.alchemicalBackwardLabel1 = QLabel('Backward:')
1444
+ self.alchemicalBackwardLineEdit1 = QLineEdit()
1445
+ self.alchemicalBackwardButton1 = QPushButton('Browse')
1446
+ self.alchemicalBoundStateLayout.addWidget(self.alchemicalBackwardLabel1, 1, 0)
1447
+ self.alchemicalBoundStateLayout.addWidget(self.alchemicalBackwardLineEdit1, 1, 1)
1448
+ self.alchemicalBoundStateLayout.addWidget(self.alchemicalBackwardButton1, 1, 2)
1449
+
1450
+ self.alchemicalBoundStateLabel2 = QLabel('Restraints/Bound state (.log):')
1451
+ self.alchemicalBoundStateLayout2 = QGridLayout()
1452
+
1453
+ self.alchemicalForwardLabel2 = QLabel('Forward:')
1454
+ self.alchemicalForwardLineEdit2 = QLineEdit()
1455
+ self.alchemicalForwardButton2 = QPushButton('Browse')
1456
+ self.alchemicalBoundStateLayout2.addWidget(self.alchemicalForwardLabel2, 0, 0)
1457
+ self.alchemicalBoundStateLayout2.addWidget(self.alchemicalForwardLineEdit2, 0, 1)
1458
+ self.alchemicalBoundStateLayout2.addWidget(self.alchemicalForwardButton2, 0, 2)
1459
+
1460
+ self.alchemicalBackwardLabel2 = QLabel('Backward:')
1461
+ self.alchemicalBackwardLineEdit2 = QLineEdit()
1462
+ self.alchemicalBackwardButton2 = QPushButton('Browse')
1463
+ self.alchemicalBoundStateLayout2.addWidget(self.alchemicalBackwardLabel2, 1, 0)
1464
+ self.alchemicalBoundStateLayout2.addWidget(self.alchemicalBackwardLineEdit2, 1, 1)
1465
+ self.alchemicalBoundStateLayout2.addWidget(self.alchemicalBackwardButton2, 1, 2)
1466
+
1467
+ # unbound state
1468
+
1469
+ self.alchemicalUnboundStateLabel = QLabel('Atoms/Unbound state (.fepout/.pmf):')
1470
+ self.alchemicalUnboundStateLayout = QGridLayout()
1471
+
1472
+ self.alchemicalForwardLabel3 = QLabel('Forward:')
1473
+ self.alchemicalForwardLineEdit3 = QLineEdit()
1474
+ self.alchemicalForwardButton3 = QPushButton('Browse')
1475
+ self.alchemicalUnboundStateLayout.addWidget(self.alchemicalForwardLabel3, 0, 0)
1476
+ self.alchemicalUnboundStateLayout.addWidget(self.alchemicalForwardLineEdit3, 0, 1)
1477
+ self.alchemicalUnboundStateLayout.addWidget(self.alchemicalForwardButton3, 0, 2)
1478
+
1479
+ self.alchemicalBackwardLabel3 = QLabel('Backward:')
1480
+ self.alchemicalBackwardLineEdit3 = QLineEdit()
1481
+ self.alchemicalBackwardButton3 = QPushButton('Browse')
1482
+ self.alchemicalUnboundStateLayout.addWidget(self.alchemicalBackwardLabel3, 1, 0)
1483
+ self.alchemicalUnboundStateLayout.addWidget(self.alchemicalBackwardLineEdit3, 1, 1)
1484
+ self.alchemicalUnboundStateLayout.addWidget(self.alchemicalBackwardButton3, 1, 2)
1485
+
1486
+ self.alchemicalUnboundStateLabel2 = QLabel('Restraints/Unbound state (.log):')
1487
+ self.alchemicalUnboundStateLayout2 = QGridLayout()
1488
+
1489
+ self.alchemicalForwardLabel4 = QLabel('Forward:')
1490
+ self.alchemicalForwardLineEdit4 = QLineEdit()
1491
+ self.alchemicalForwardButton4 = QPushButton('Browse')
1492
+ self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalForwardLabel4, 0, 0)
1493
+ self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalForwardLineEdit4, 0, 1)
1494
+ self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalForwardButton4, 0, 2)
1495
+
1496
+ self.alchemicalBackwardLabel4 = QLabel('Backward:')
1497
+ self.alchemicalBackwardLineEdit4 = QLineEdit()
1498
+ self.alchemicalBackwardButton4 = QPushButton('Browse')
1499
+ self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalBackwardLabel4, 1, 0)
1500
+ self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalBackwardLineEdit4, 1, 1)
1501
+ self.alchemicalUnboundStateLayout2.addWidget(self.alchemicalBackwardButton4, 1, 2)
1502
+
1503
+ self.restraintInputsLayout.addWidget(self.alchemicalBoundStateLabel)
1504
+ self.restraintInputsLayout.addLayout(self.alchemicalBoundStateLayout)
1505
+ self.restraintInputsLayout.addWidget(self.alchemicalBoundStateLabel2)
1506
+ self.restraintInputsLayout.addLayout(self.alchemicalBoundStateLayout2)
1507
+ self.restraintInputsLayout.addWidget(self.alchemicalUnboundStateLabel)
1508
+ self.restraintInputsLayout.addLayout(self.alchemicalUnboundStateLayout)
1509
+ self.restraintInputsLayout.addWidget(self.alchemicalUnboundStateLabel2)
1510
+ self.restraintInputsLayout.addLayout(self.alchemicalUnboundStateLayout2)
1511
+ self.restraintInputs.setLayout(self.restraintInputsLayout)
1512
+
1513
+ # alchemical force constants
1514
+ self.alchemicalForceConstants = QGroupBox('Force constants (in Colvars unit):')
1515
+
1516
+ # all widgets
1517
+ self.alchemicalFCLayout = QHBoxLayout()
1518
+ self.alchemicalfcThetaLabel = QLabel('Theta:')
1519
+ self.alchemicalfcThetaLineEdit = QLineEdit('0.1')
1520
+ self.alchemicalfcPhiLabel = QLabel(' Phi: ')
1521
+ self.alchemicalfcPhiLineEdit = QLineEdit('0.1')
1522
+ self.alchemicalfcPsiLabel = QLabel('Psi: ')
1523
+ self.alchemicalfcPsiLineEdit = QLineEdit('0.1')
1524
+ self.alchemicalfcthetaLabel = QLabel('theta:')
1525
+ self.alchemicalfcthetaLineEdit = QLineEdit('0.1')
1526
+ self.alchemicalfcphiLabel = QLabel(' phi: ')
1527
+ self.alchemicalfcphiLineEdit = QLineEdit('0.1')
1528
+ self.alchemicalfcRLabel = QLabel('r: ')
1529
+ self.alchemicalfcRLineEdit = QLineEdit('10')
1530
+
1531
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcThetaLabel)
1532
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcThetaLineEdit)
1533
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcPhiLabel)
1534
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcPhiLineEdit)
1535
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcPsiLabel)
1536
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcPsiLineEdit)
1537
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcthetaLabel)
1538
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcthetaLineEdit)
1539
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcphiLabel)
1540
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcphiLineEdit)
1541
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcRLabel)
1542
+ self.alchemicalFCLayout.addWidget(self.alchemicalfcRLineEdit)
1543
+
1544
+ self.alchemicalForceConstants.setLayout(self.alchemicalFCLayout)
1545
+
1546
+ # alchemical restraint centers
1547
+ self.alchemicalRestraintCenters = QGroupBox('Restraint centers (in Colvars unit), temperature and others:')
1548
+
1549
+ # all widgets
1550
+ self.alchemicalRCLayout = QHBoxLayout()
1551
+ self.alchemicalRCThetaLabel = QLabel('Theta:')
1552
+ self.alchemicalRCThetaLineEdit = QLineEdit('0')
1553
+ self.alchemicalRCthetaLabel = QLabel('theta:')
1554
+ self.alchemicalRCthetaLineEdit = QLineEdit('90')
1555
+ self.alchemicalRCRLabel = QLabel('r: ')
1556
+ self.alchemicalRCRLineEdit = QLineEdit('8')
1557
+ self.alchemicalPostTemperatureLabel = QLabel('temperature:')
1558
+ self.alchemicalPostTemperatureLineEdit = QLineEdit('300')
1559
+ self.alchemicalPostTypeLabel = QLabel('Post-treatment type:')
1560
+ self.alchemicalPostTypeBox = QComboBox()
1561
+ self.alchemicalPostTypeBox.addItem('FEP')
1562
+ self.alchemicalPostTypeBox.addItem('BAR')
1563
+ self.alchemicalPostTypeBox.addItem('PMF')
1564
+ self.alchemicalPostTypeBox.setCurrentIndex(1)
1565
+
1566
+ self.alchemicalRCLayout.addWidget(self.alchemicalRCThetaLabel)
1567
+ self.alchemicalRCLayout.addWidget(self.alchemicalRCThetaLineEdit)
1568
+ self.alchemicalRCLayout.addWidget(self.alchemicalRCthetaLabel)
1569
+ self.alchemicalRCLayout.addWidget(self.alchemicalRCthetaLineEdit)
1570
+ self.alchemicalRCLayout.addWidget(self.alchemicalRCRLabel)
1571
+ self.alchemicalRCLayout.addWidget(self.alchemicalRCRLineEdit)
1572
+ self.alchemicalRCLayout.addWidget(self.alchemicalPostTemperatureLabel)
1573
+ self.alchemicalRCLayout.addWidget(self.alchemicalPostTemperatureLineEdit)
1574
+ self.alchemicalRCLayout.addWidget(self.alchemicalPostTypeLabel)
1575
+ self.alchemicalRCLayout.addWidget(self.alchemicalPostTypeBox)
1576
+
1577
+ self.alchemicalRestraintCenters.setLayout(self.alchemicalRCLayout)
1578
+
1579
+ self.alchemicalTabLayout.addWidget(self.restraintInputs)
1580
+ self.alchemicalTabLayout.addWidget(self.alchemicalForceConstants)
1581
+ self.alchemicalTabLayout.addWidget(self.alchemicalRestraintCenters)
1582
+ self.alchemicalTab.setLayout(self.alchemicalTabLayout)
1583
+
1584
+ def _initLDDMTab(self):
1585
+ """initialize LDDM tab of post-treatment
1586
+ """
1587
+
1588
+ self.LDDMTab = QWidget()
1589
+ self.LDDMTabLayout = QVBoxLayout()
1590
+
1591
+
1592
+ self.LDDMSimulationInputs = QGroupBox('Inputs for LDDM simulations:')
1593
+ self.LDDMSimulationInputsLayout = QVBoxLayout()
1594
+
1595
+ # step 1
1596
+ self.LDDMStep1Label = QLabel('Step 1:')
1597
+ self.LDDMStep1Layout = QGridLayout()
1598
+
1599
+ self.LDDMStep1ColvarsLabel = QLabel('colvars.in.tmp file:')
1600
+ self.LDDMStep1ColvarsLineEdit = QLineEdit()
1601
+ self.LDDMStep1ColvarsButton = QPushButton('Browse')
1602
+ self.LDDMStep1Layout.addWidget(self.LDDMStep1ColvarsLabel, 0, 0)
1603
+ self.LDDMStep1Layout.addWidget(self.LDDMStep1ColvarsLineEdit, 0, 1)
1604
+ self.LDDMStep1Layout.addWidget(self.LDDMStep1ColvarsButton, 0, 2)
1605
+
1606
+ self.LDDMStep1ColvarsTrajLabel = QLabel('colvars.traj file:')
1607
+ self.LDDMStep1ColvarsTrajLineEdit = QLineEdit()
1608
+ self.LDDMStep1ColvarsTrajButton = QPushButton('Browse')
1609
+ self.LDDMStep1Layout.addWidget(self.LDDMStep1ColvarsTrajLabel, 1, 0)
1610
+ self.LDDMStep1Layout.addWidget(self.LDDMStep1ColvarsTrajLineEdit, 1, 1)
1611
+ self.LDDMStep1Layout.addWidget(self.LDDMStep1ColvarsTrajButton, 1, 2)
1612
+
1613
+ self.LDDMStep1FepoutLabel = QLabel('fepout file:')
1614
+ self.LDDMStep1FepoutLineEdit = QLineEdit()
1615
+ self.LDDMStep1FepoutButton = QPushButton('Browse')
1616
+ self.LDDMStep1Layout.addWidget(self.LDDMStep1FepoutLabel, 2, 0)
1617
+ self.LDDMStep1Layout.addWidget(self.LDDMStep1FepoutLineEdit, 2, 1)
1618
+ self.LDDMStep1Layout.addWidget(self.LDDMStep1FepoutButton, 2, 2)
1619
+
1620
+ self.LDDMStep3Label = QLabel('Step 3:')
1621
+ self.LDDMStep3Layout = QGridLayout()
1622
+
1623
+ self.LDDMStep3FepoutLabel = QLabel('fepout file: ')
1624
+ self.LDDMStep3FepoutLineEdit = QLineEdit()
1625
+ self.LDDMStep3FepoutButton = QPushButton('Browse')
1626
+ self.LDDMStep3Layout.addWidget(self.LDDMStep3FepoutLabel, 0, 0)
1627
+ self.LDDMStep3Layout.addWidget(self.LDDMStep3FepoutLineEdit, 0, 1)
1628
+ self.LDDMStep3Layout.addWidget(self.LDDMStep3FepoutButton, 0, 2)
1629
+
1630
+ self.LDDMSimulationInputsLayout.addWidget(self.LDDMStep1Label)
1631
+ self.LDDMSimulationInputsLayout.addLayout(self.LDDMStep1Layout)
1632
+ self.LDDMSimulationInputsLayout.addWidget(self.LDDMStep3Label)
1633
+ self.LDDMSimulationInputsLayout.addLayout(self.LDDMStep3Layout)
1634
+ self.LDDMSimulationInputs.setLayout(self.LDDMSimulationInputsLayout)
1635
+
1636
+ # other parameter
1637
+ self.LDDMParameters = QGroupBox('Other parameters:')
1638
+
1639
+ # all widgets
1640
+ self.LDDMParametersLayout = QGridLayout()
1641
+ self.LDDMStep1StepsPerWindowLabel = QLabel('Steps per window (Step 1):')
1642
+ self.LDDMStep1StepsPerWindowLineEdit = QLineEdit('500000')
1643
+ self.LDDMStep1EquilStepsPerWindowLabel = QLabel('Equilbration per window (Step 1): ')
1644
+ self.LDDMStep1EquilStepsPerWindowLineEdit = QLineEdit('100000')
1645
+ self.LDDMStep1WindowsLabel = QLabel('Windows (Step 1): ')
1646
+ self.LDDMStep1WindowsLineEdit = QLineEdit('200')
1647
+ self.LDDMPostTemperatureLabel = QLabel('temperature:')
1648
+ self.LDDMPostTemperatureLineEdit = QLineEdit('300')
1649
+ self.LDDMPostTypeLabel = QLabel('Post-treatment type:')
1650
+ self.LDDMPostTypeBox = QComboBox()
1651
+ self.LDDMPostTypeBox.addItem('FEP')
1652
+ self.LDDMPostTypeBox.addItem('BAR')
1653
+ self.LDDMPostTypeBox.setCurrentIndex(1)
1654
+
1655
+ self.LDDMParametersLayout.addWidget(self.LDDMStep1StepsPerWindowLabel, 0, 0)
1656
+ self.LDDMParametersLayout.addWidget(self.LDDMStep1StepsPerWindowLineEdit, 0, 1)
1657
+ self.LDDMParametersLayout.addWidget(self.LDDMStep1WindowsLabel, 0, 2)
1658
+ self.LDDMParametersLayout.addWidget(self.LDDMStep1WindowsLineEdit, 0, 3)
1659
+ self.LDDMParametersLayout.addWidget(self.LDDMStep1EquilStepsPerWindowLabel, 1, 0)
1660
+ self.LDDMParametersLayout.addWidget(self.LDDMStep1EquilStepsPerWindowLineEdit, 1, 1)
1661
+ self.LDDMParametersLayout.addWidget(self.LDDMPostTemperatureLabel, 1, 2)
1662
+ self.LDDMParametersLayout.addWidget(self.LDDMPostTemperatureLineEdit, 1, 3)
1663
+ self.LDDMParametersLayout.addWidget(self.LDDMPostTypeLabel, 2, 0)
1664
+ self.LDDMParametersLayout.addWidget(self.LDDMPostTypeBox, 2, 1)
1665
+
1666
+ self.LDDMParameters.setLayout(self.LDDMParametersLayout)
1667
+
1668
+ self.LDDMTabLayout.addWidget(self.LDDMSimulationInputs)
1669
+ self.LDDMTabLayout.addWidget(self.LDDMParameters)
1670
+ self.LDDMTab.setLayout(self.LDDMTabLayout)
1671
+
1672
+ def _initQuickPlotTab(self):
1673
+ """initialize quick-plot tab
1674
+ """
1675
+
1676
+ self.quickPlot = QWidget()
1677
+ self.quickPlotLayout = QVBoxLayout()
1678
+
1679
+ # plot a (stratified) pmf
1680
+ self.plotPmf = QGroupBox('Plot (stratified) PMFs:')
1681
+ self.plotPmfLayout = QVBoxLayout()
1682
+
1683
+ self.plotPmfLabel = QLabel('PMF files:')
1684
+ self.plotPmfBox = QListWidget()
1685
+ self.plotPmfChildLayout = QHBoxLayout()
1686
+ self.plotPmfAddButton = QPushButton('Add')
1687
+ self.plotPmfClearButton = QPushButton('Clear')
1688
+ self.plotPmfPlotButton = QPushButton('Plot')
1689
+ self.plotPmfChildLayout.addWidget(self.plotPmfAddButton)
1690
+ self.plotPmfChildLayout.addWidget(self.plotPmfClearButton)
1691
+ self.plotPmfChildLayout.addWidget(self.plotPmfPlotButton)
1692
+
1693
+ self.plotPmfLayout.addWidget(self.plotPmfLabel)
1694
+ self.plotPmfLayout.addWidget(self.plotPmfBox)
1695
+ self.plotPmfLayout.addLayout(self.plotPmfChildLayout)
1696
+ self.plotPmf.setLayout(self.plotPmfLayout)
1697
+
1698
+ # calculate pmf RMSD convergence
1699
+ self.plotPmfConvergence = QGroupBox('Calculate PMF RMSD convergence:')
1700
+ self.plotPmfConvergenceLayout = QVBoxLayout()
1701
+
1702
+ self.plotPmfConvergenceLabel = QLabel('history file:')
1703
+ self.plotPmfConvergenceBox = QLineEdit()
1704
+ self.plotPmfConvergenceChildLayout = QHBoxLayout()
1705
+ self.plotPmfConvergenceBrowseButton = QPushButton('Browse')
1706
+ self.plotPmfConvergencePlotButton = QPushButton('Plot')
1707
+ self.plotPmfConvergenceChildLayout.addWidget(self.plotPmfConvergenceBrowseButton)
1708
+ self.plotPmfConvergenceChildLayout.addWidget(self.plotPmfConvergencePlotButton)
1709
+
1710
+ self.plotPmfConvergenceLayout.addWidget(self.plotPmfConvergenceLabel)
1711
+ self.plotPmfConvergenceLayout.addWidget(self.plotPmfConvergenceBox)
1712
+ self.plotPmfConvergenceLayout.addLayout(self.plotPmfConvergenceChildLayout)
1713
+ self.plotPmfConvergence.setLayout(self.plotPmfConvergenceLayout)
1714
+
1715
+ # merge a (stratified) pmf
1716
+ self.mergePmf = QGroupBox('Merge (stratified) PMFs:')
1717
+ self.mergePmfLayout = QVBoxLayout()
1718
+
1719
+ self.mergePmfLabel = QLabel('PMF files:')
1720
+ self.mergePmfBox = QListWidget()
1721
+ self.mergePmfChildLayout = QHBoxLayout()
1722
+ self.mergePmfAddButton = QPushButton('Add')
1723
+ self.mergePmfClearButton = QPushButton('Clear')
1724
+ self.mergePmfmergeButton = QPushButton('Merge')
1725
+ self.mergePmfChildLayout.addWidget(self.mergePmfAddButton)
1726
+ self.mergePmfChildLayout.addWidget(self.mergePmfClearButton)
1727
+ self.mergePmfChildLayout.addWidget(self.mergePmfmergeButton)
1728
+
1729
+ self.mergePmfLayout.addWidget(self.mergePmfLabel)
1730
+ self.mergePmfLayout.addWidget(self.mergePmfBox)
1731
+ self.mergePmfLayout.addLayout(self.mergePmfChildLayout)
1732
+ self.mergePmf.setLayout(self.mergePmfLayout)
1733
+
1734
+ # plot hysteresis between forward and backward simulations
1735
+ self.plotHysteresis = QGroupBox('Plot hysteresis between bidirectional simulations:')
1736
+ self.plotHysteresisLayout = QVBoxLayout()
1737
+
1738
+ self.plotHysteresisForwardLayout = QHBoxLayout()
1739
+ self.plotHysteresisForwardLabel = QLabel('Forward (fepout/log): ')
1740
+ self.plotHysteresisForwardLineEdit = QLineEdit()
1741
+ self.plotHysteresisForwardButton = QPushButton('Browse')
1742
+ self.plotHysteresisForwardLayout.addWidget(self.plotHysteresisForwardLabel)
1743
+ self.plotHysteresisForwardLayout.addWidget(self.plotHysteresisForwardLineEdit)
1744
+ self.plotHysteresisForwardLayout.addWidget(self.plotHysteresisForwardButton)
1745
+
1746
+ self.plotHysteresisBackwardLayout = QHBoxLayout()
1747
+ self.plotHysteresisBackwardLabel = QLabel('Backward (fepout/log):')
1748
+ self.plotHysteresisBackwardLineEdit = QLineEdit()
1749
+ self.plotHysteresisBackwardButton = QPushButton('Browse')
1750
+ self.plotHysteresisBackwardLayout.addWidget(self.plotHysteresisBackwardLabel)
1751
+ self.plotHysteresisBackwardLayout.addWidget(self.plotHysteresisBackwardLineEdit)
1752
+ self.plotHysteresisBackwardLayout.addWidget(self.plotHysteresisBackwardButton)
1753
+
1754
+ self.plotHysteresisPlotButton = QPushButton('Plot')
1755
+
1756
+ self.plotHysteresisLayout.addLayout(self.plotHysteresisForwardLayout)
1757
+ self.plotHysteresisLayout.addLayout(self.plotHysteresisBackwardLayout)
1758
+
1759
+ self.isRigidLigandCheckbox = QCheckBox('Rigid ligand (ligand RMSD restraints):')
1760
+ self.isRigidLigandCheckbox.setChecked(False)
1761
+
1762
+ self.plotHysteresisLayout.addWidget(self.isRigidLigandCheckbox)
1763
+ self.plotHysteresisLayout.addWidget(self.plotHysteresisPlotButton)
1764
+
1765
+ self.plotHysteresis.setLayout(self.plotHysteresisLayout)
1766
+
1767
+ self.quickPlotLayout.addWidget(self.plotPmf)
1768
+ self.quickPlotLayout.addWidget(self.mergePmf)
1769
+ self.quickPlotLayout.addWidget(self.plotPmfConvergence)
1770
+ self.quickPlotLayout.addWidget(self.plotHysteresis)
1771
+ self.quickPlot.setLayout(self.quickPlotLayout)
1772
+
1773
+ # slots are defined below
1774
+ # otherwise they are defined in slots.py
1775
+ def _mainSettings(self):
1776
+ """call main settings
1777
+ """
1778
+ def f():
1779
+ self.mainSettings.show()
1780
+ return f
1781
+
1782
+ def _advancedSettings(self, comboBox):
1783
+ """call advanced settings in pre-treatment
1784
+ the returned function is depended on the comboBox(strategy type)
1785
+ """
1786
+
1787
+ def f():
1788
+ if comboBox.currentText() == 'Geometric':
1789
+ self.geometricAdvancedSettings.show()
1790
+ elif comboBox.currentText() == 'Alchemical':
1791
+ self.alchemicalAdvancedSettings.show()
1792
+
1793
+ return f
1794
+
1795
+ def _changeFFButtonState(self):
1796
+ """enable/disable the add and clear button of force field section
1797
+ """
1798
+
1799
+ if self.forceFieldCombobox.currentText() == 'CHARMM':
1800
+ self.forceFieldAddButton.setEnabled(True)
1801
+ self.forceFieldClearButton.setEnabled(True)
1802
+ self.forceFieldFilesBox.setEnabled(True)
1803
+ elif self.forceFieldCombobox.currentText() == 'Amber':
1804
+ self.forceFieldAddButton.setEnabled(False)
1805
+ self.forceFieldClearButton.setEnabled(False)
1806
+ self.forceFieldFilesBox.setEnabled(False)
1807
+
1808
+ def _changeStrategySettingState(self):
1809
+ """enable/disable the alchemical route and some advanced settings based on the MD engine
1810
+ """
1811
+
1812
+ if self.selectMDEngineCombobox.currentText() == 'NAMD':
1813
+ self.selectStrategyCombobox.setEnabled(True)
1814
+ self.geometricAdvancedSettings.useOldCvCheckbox.setEnabled(True)
1815
+ self.geometricAdvancedSettings.OPLSMixingRuleCheckbox.setEnabled(True)
1816
+ self.geometricAdvancedSettings.useGaWTMCheckbox.setEnabled(True)
1817
+
1818
+ elif self.selectMDEngineCombobox.currentText() == 'Gromacs':
1819
+ index = self.selectStrategyCombobox.findText('Geometric', QtCore.Qt.MatchFixedString)
1820
+ if index >= 0:
1821
+ self.selectStrategyCombobox.setCurrentIndex(index)
1822
+ self.selectStrategyCombobox.setEnabled(False)
1823
+
1824
+ self.geometricAdvancedSettings.useOldCvCheckbox.setChecked(False)
1825
+ self.geometricAdvancedSettings.useOldCvCheckbox.setEnabled(False)
1826
+
1827
+ self.geometricAdvancedSettings.OPLSMixingRuleCheckbox.setChecked(False)
1828
+ self.geometricAdvancedSettings.OPLSMixingRuleCheckbox.setEnabled(False)
1829
+
1830
+ self.geometricAdvancedSettings.useGaWTMCheckbox.setEnabled(False)
1831
+ self.geometricAdvancedSettings.useGaWTMCheckbox.setChecked(False)
1832
+
1833
+ def _changeStrategySettingStateForOldGromacs(self):
1834
+ """enable/disable a lot of options for the old Gromacs tab
1835
+ """
1836
+
1837
+ if self.preTreatmentMainTabs.currentWidget() == self.NAMDTab:
1838
+ self.selectStrategyAdvancedButton.setEnabled(True)
1839
+ self.selectMDEngineCombobox.setEnabled(True)
1840
+
1841
+ elif self.preTreatmentMainTabs.currentWidget() == self.GromacsTab:
1842
+ index = self.selectMDEngineCombobox.findText('Gromacs', QtCore.Qt.MatchFixedString)
1843
+ if index >= 0:
1844
+ self.selectMDEngineCombobox.setCurrentIndex(index)
1845
+
1846
+ index = self.selectStrategyCombobox.findText('Geometric', QtCore.Qt.MatchFixedString)
1847
+ if index >= 0:
1848
+ self.selectStrategyCombobox.setCurrentIndex(index)
1849
+
1850
+ self.selectMDEngineCombobox.setEnabled(False)
1851
+ self.selectStrategyCombobox.setEnabled(False)
1852
+ self.selectStrategyAdvancedButton.setEnabled(False)
1853
+
1854
+ def _openDocFile(self):
1855
+ """open Documentation file
1856
+ """
1857
+
1858
+ webbrowser.open('https://www.nature.com/articles/s41596-021-00676-1')
1859
+
1860
+ def _openPythonAPIFile(self):
1861
+ """open Python API Documentation file
1862
+ """
1863
+
1864
+ webbrowser.open('https://fhh2626.github.io/BFEE2APIDocs/')
1865
+
1866
+ def _showAboutBox(self):
1867
+ """the about message box
1868
+
1869
+ Returns:
1870
+ function obj: a slot function that shows that about message box
1871
+ """
1872
+
1873
+ def f():
1874
+ QMessageBox.about(
1875
+ self,
1876
+ 'About',
1877
+ f'<center><b>{__PROGRAM_NAME__}</b></center><br>'+r'''
1878
+ Binding free energy estimator (BFEE) is a python-based software
1879
+ that automates absolute binding free energy calculations through
1880
+ either the alchemical or geometric route by molecular dynamics
1881
+ simulations.<br>
1882
+ <b>Authors:</b><br>
1883
+ Haohao Fu (<a href="mailto:fhh2626@gmail.com">fhh2626@gmail.com</a>)<br>
1884
+ Haochuan Chen (<a href="mailto:summersnow9403@gmail.com">summersnow9403@gmail.com</a>)<br>
1885
+ <b>License:</b><br>
1886
+ BFEE2 is free software: you can redistribute it and/or modify it
1887
+ under the terms of the GNU General Public License as published by
1888
+ the Free Software Foundation, either version 3 of the License, or
1889
+ (at your option) any later version.<br>
1890
+ <b>Reference:</b><br>
1891
+ <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>
1892
+ <b>Alchemical and geometric routes:</b> Gumbart et al. J. Chem. Theory Comput. 2013, 9, 794-802<br>
1893
+ <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>
1894
+ <b>GaWTM-eABF:</b> Chen et al. J. Chem. Theory Comput. 2021, 17, 3886-3894<br>
1895
+ <b>Collective variables:</b> Fu et al. J. Chem. Theory Comput. 2017, 13, 5173-5178<br>
1896
+ <b>Streamlined geometric route:</b> Fu et al. J. Chem. Inf. Model. 2023, 63, 2512-2519<br>
1897
+ ''')
1898
+ return f
1899
+
1900
+ def _showGeometricResults(self, unit):
1901
+ ''' calculate binding from the geometric route,
1902
+ parameters in the Geometric tab will be read.
1903
+ Show a QMessageBox for the result
1904
+ Inputs:
1905
+ unit (string): 'namd' or 'gromacs' '''
1906
+
1907
+ pTreat = postTreatment.postTreatment(
1908
+ float(self.postTemperatureLineEdit.text()), unit, 'geometric')
1909
+
1910
+ pmfs = [
1911
+ self.rmsdBoundLineEdit.text(),
1912
+ self.ThetaLineEdit.text(),
1913
+ self.PhiLineEdit.text(),
1914
+ self.PsiLineEdit.text(),
1915
+ self.thetaLineEdit.text(),
1916
+ self.phiLineEdit.text(),
1917
+ self.rLineEdit.text(),
1918
+ self.rmsdUnboundLineEdit.text()
1919
+ ]
1920
+
1921
+ try:
1922
+ parameters = [
1923
+ float(self.fcRMSDLineEdit.text()),
1924
+ float(self.fcThetaLineEdit.text()),
1925
+ float(self.fcPhiLineEdit.text()),
1926
+ float(self.fcPsiLineEdit.text()),
1927
+ float(self.fcthetaLineEdit.text()),
1928
+ float(self.fcphiLineEdit.text()),
1929
+ float(self.postRstarLineEdit.text()),
1930
+ float(self.fcRMSDLineEdit.text())
1931
+ ]
1932
+ except:
1933
+ QMessageBox.warning(self, 'Error', f'Force constant or r* input error!')
1934
+ return
1935
+
1936
+ # check inputs
1937
+ for index, item in enumerate(pmfs):
1938
+ if (index != 0) and (index != 7) and (not os.path.exists(item)):
1939
+ QMessageBox.warning(self, 'Error', f'file {item} does not exist!')
1940
+ return
1941
+ if (index == 7) and (not os.path.exists(item)):
1942
+ QMessageBox.warning(self, 'Warning', f'Supposing dealing with a rigid ligand!')
1943
+
1944
+ # calculate free energies
1945
+ try:
1946
+ result = pTreat.geometricBindingFreeEnergy(pmfs, parameters)
1947
+ except postTreatment.RStarTooLargeError:
1948
+ QMessageBox.warning(
1949
+ self,
1950
+ 'Error',
1951
+ f'\
1952
+ r_star cannot be larger than r_max of step 7!\n'
1953
+ )
1954
+ return
1955
+ except Exception as e:
1956
+ print(e)
1957
+ QMessageBox.warning(
1958
+ self,
1959
+ 'Error',
1960
+ f'\
1961
+ Unknown error! The error message is: \n\
1962
+ {e}\n'
1963
+ )
1964
+ return
1965
+
1966
+ QMessageBox.about(
1967
+ self,
1968
+ 'Result',
1969
+ f'\
1970
+ Results:\n\
1971
+ ΔG(site,c) = {result[0]:.2f} kcal/mol\n\
1972
+ ΔG(site,eulerTheta) = {result[1]:.2f} kcal/mol\n\
1973
+ ΔG(site,eulerPhi) = {result[2]:.2f} kcal/mol\n\
1974
+ ΔG(site,eulerPsi) = {result[3]:.2f} kcal/mol\n\
1975
+ ΔG(site,polarTheta) = {result[4]:.2f} kcal/mol\n\
1976
+ ΔG(site,polarPhi) = {result[5]:.2f} kcal/mol\n\
1977
+ (1/beta)*ln(S*I*C0) = {result[6]:.2f} kcal/mol\n\
1978
+ ΔG(bulk,c) = {result[7]:.2f} kcal/mol\n\
1979
+ ΔG(bulk,o) = {result[8]:.2f} kcal/mol\n\
1980
+ \n\
1981
+ Standard Binding Free Energy:\n\
1982
+ ΔG(total) = {result[9]:.2f} kcal/mol\n'
1983
+ )
1984
+
1985
+ def _showAlchemicalResults(self, unit):
1986
+ """calculate binding from the alchemical route,
1987
+ parameters in the alchemical tab will be read.
1988
+ Show a QMessageBox for the result
1989
+
1990
+ Args:
1991
+ unit (str): 'namd' or 'gromacs'
1992
+ """
1993
+
1994
+ pTreat = postTreatment.postTreatment(
1995
+ float(self.alchemicalPostTemperatureLineEdit.text()), unit, 'alchemical')
1996
+
1997
+ # alchemical outputs
1998
+ files = [
1999
+ self.alchemicalForwardLineEdit1.text(),
2000
+ self.alchemicalBackwardLineEdit1.text(),
2001
+ self.alchemicalForwardLineEdit2.text(),
2002
+ self.alchemicalBackwardLineEdit2.text(),
2003
+ self.alchemicalForwardLineEdit3.text(),
2004
+ self.alchemicalBackwardLineEdit3.text(),
2005
+ self.alchemicalForwardLineEdit4.text(),
2006
+ self.alchemicalBackwardLineEdit4.text()
2007
+ ]
2008
+
2009
+ try:
2010
+ parameters = [
2011
+ float(self.alchemicalRCThetaLineEdit.text()),
2012
+ float(self.alchemicalRCthetaLineEdit.text()),
2013
+ float(self.alchemicalRCRLineEdit.text()),
2014
+ float(self.alchemicalfcThetaLineEdit.text()),
2015
+ float(self.alchemicalfcPhiLineEdit.text()),
2016
+ float(self.alchemicalfcPsiLineEdit.text()),
2017
+ float(self.alchemicalfcthetaLineEdit.text()),
2018
+ float(self.alchemicalfcphiLineEdit.text()),
2019
+ float(self.alchemicalfcRLineEdit.text())
2020
+ ]
2021
+ temperature = float(self.alchemicalPostTemperatureLineEdit.text())
2022
+ except:
2023
+ QMessageBox.warning(self, 'Error', f'Force constant or restraint center or temperature input error!')
2024
+ return
2025
+ if self.alchemicalPostTypeBox.currentText() == 'FEP':
2026
+ jobType = 'fep'
2027
+ elif self.alchemicalPostTypeBox.currentText() == 'BAR':
2028
+ jobType = 'bar'
2029
+ elif self.alchemicalPostTypeBox.currentText() == 'PMF':
2030
+ jobType = 'pmf'
2031
+
2032
+ # check inputs
2033
+ rigid_ligand = False
2034
+ if jobType != 'pmf':
2035
+ for index, item in enumerate([
2036
+ self.alchemicalBackwardLineEdit1.text(),
2037
+ self.alchemicalBackwardLineEdit2.text(),
2038
+ self.alchemicalBackwardLineEdit3.text(),
2039
+ self.alchemicalBackwardLineEdit4.text()
2040
+ ]):
2041
+ if (not os.path.exists(item)) and (index != 3):
2042
+ QMessageBox.warning(self, 'Error', f'backward file {item} does not exist and is not empty!')
2043
+ return
2044
+
2045
+ for index, item in enumerate([
2046
+ self.alchemicalForwardLineEdit1.text(),
2047
+ self.alchemicalForwardLineEdit2.text(),
2048
+ self.alchemicalForwardLineEdit3.text(),
2049
+ self.alchemicalForwardLineEdit4.text(),
2050
+ ]):
2051
+ if (not os.path.exists(item)) and (index != 3):
2052
+ QMessageBox.warning(self, 'Error', f'file {item} does not exist!')
2053
+ return
2054
+ elif (not os.path.exists(item)) and (index == 3):
2055
+ QMessageBox.warning(self, 'Warning', f'Supposing dealing with a rigid ligand!')
2056
+ rigid_ligand = True
2057
+
2058
+ # calculate free energies
2059
+ result, errors = pTreat.alchemicalBindingFreeEnergy(files, parameters, temperature, jobType, rigid_ligand)
2060
+
2061
+ QMessageBox.about(
2062
+ self,
2063
+ 'Result',
2064
+ f'\
2065
+ Results:\n\
2066
+ ΔG(site,couple) = {result[0]:.2f} ± {errors[0]:.2f} kcal/mol\n\
2067
+ ΔG(site,c+o+a+r) = {result[1]:.2f} ± {errors[1]:.2f} kcal/mol\n\
2068
+ ΔG(bulk,decouple) = {result[2]:.2f} ± {errors[2]:.2f} kcal/mol\n\
2069
+ ΔG(bulk,c) = {result[3]:.2f} ± {errors[3]:.2f} kcal/mol\n\
2070
+ ΔG(bulk,o+a+r) = {result[4]:.2f} kcal/mol\n\
2071
+ \n\
2072
+ Standard Binding Free Energy:\n\
2073
+ ΔG(total) = {result[5]:.2f} ± {errors[5]:.2f} kcal/mol\n'
2074
+ )
2075
+
2076
+ def _showLDDMResults(self, unit):
2077
+ """calculate binding from the LDDM route,
2078
+ parameters in the LDDM tab will be read.
2079
+ Show a QMessageBox for the result
2080
+
2081
+ Args:
2082
+ unit (str): 'namd' or 'gromacs'
2083
+ """
2084
+
2085
+ pTreat = postTreatment.postTreatment(
2086
+ float(self.LDDMPostTemperatureLineEdit.text()), unit, 'LDDM')
2087
+
2088
+ # alchemical outputs
2089
+ try:
2090
+ temperature = float(self.LDDMPostTemperatureLineEdit.text())
2091
+ except:
2092
+ QMessageBox.warning(self, 'Error', f'temperature input error!')
2093
+ return
2094
+ if self.LDDMPostTypeBox.currentText() == 'FEP':
2095
+ jobType = 'fep'
2096
+ elif self.LDDMPostTypeBox.currentText() == 'BAR':
2097
+ jobType = 'bar'
2098
+
2099
+ # check inputs
2100
+ rigid_ligand = False
2101
+ for index, item in enumerate([
2102
+ self.LDDMStep1ColvarsLineEdit.text(),
2103
+ self.LDDMStep1ColvarsTrajLineEdit.text(),
2104
+ self.LDDMStep1FepoutLineEdit.text(),
2105
+ self.LDDMStep3FepoutLineEdit.text()
2106
+ ]):
2107
+ if (not os.path.exists(item)) and (index != 3):
2108
+ QMessageBox.warning(self, 'Error', f'{item} does not exist and is not empty!')
2109
+ return
2110
+
2111
+ try:
2112
+ int(self.LDDMStep1StepsPerWindowLineEdit.text())
2113
+ int(self.LDDMStep1EquilStepsPerWindowLineEdit.text())
2114
+ int(self.LDDMStep1WindowsLineEdit.text())
2115
+ except:
2116
+ QMessageBox.warning(self, 'Error', f'Invalid value in LDDM parameters!')
2117
+ return
2118
+
2119
+ # calculate free energies
2120
+ try:
2121
+ step1_result, step3_result = pTreat.LDDMBindingFreeEnergy(
2122
+ self.LDDMStep1ColvarsLineEdit.text(),
2123
+ self.LDDMStep1ColvarsTrajLineEdit.text(),
2124
+ self.LDDMStep1FepoutLineEdit.text(),
2125
+ int(self.LDDMStep1StepsPerWindowLineEdit.text()),
2126
+ int(self.LDDMStep1EquilStepsPerWindowLineEdit.text()),
2127
+ int(self.LDDMStep1WindowsLineEdit.text()) + 1,
2128
+ self.LDDMStep3FepoutLineEdit.text(),
2129
+ temperature = temperature,
2130
+ jobType = jobType
2131
+ )
2132
+ except Exception as e:
2133
+ print(e)
2134
+ QMessageBox.warning(
2135
+ self,
2136
+ 'Error',
2137
+ f'\
2138
+ LDDM result calculation failed! The error message is: \n\
2139
+ {e}\n'
2140
+ )
2141
+
2142
+ QMessageBox.about(
2143
+ self,
2144
+ 'Result',
2145
+ f'\
2146
+ Results:\n\
2147
+ ΔG(Step 1) = {step1_result[0]:.2f} ± {step1_result[1]:.2f} kcal/mol\n\
2148
+ ΔG(Step 3) = {step3_result[0]:.2f} ± {step3_result[1]:.2f} kcal/mol\n\
2149
+ \n\
2150
+ Standard Binding Free Energy:\n\
2151
+ ΔG(total) = {step1_result[0] + step3_result[0]:.2f} ± {np.sqrt(step1_result[1]**2 + step3_result[1]**2):.2f} kcal/mol\n'
2152
+ )
2153
+
2154
+ def _showFinalResults(self):
2155
+ """calculate binding free energy and show the final results
2156
+
2157
+ Returns:
2158
+ function obj: a slot function that calculates binding free energy and show the final results
2159
+ """
2160
+
2161
+ def f():
2162
+ if self.postPMFTypeBox.currentText() == 'NAMD':
2163
+ unit = 'namd'
2164
+ elif self.postPMFTypeBox.currentText() == 'Gromacs':
2165
+ unit = 'gromacs'
2166
+
2167
+ if self.postTreatmentMainTabs.currentIndex() == 0:
2168
+ jobType = 'geometric'
2169
+ elif self.postTreatmentMainTabs.currentIndex() == 1:
2170
+ jobType = 'alchemical'
2171
+ elif self.postTreatmentMainTabs.currentIndex() == 2:
2172
+ jobType = 'LDDM'
2173
+
2174
+ if jobType == 'geometric':
2175
+ self._showGeometricResults(unit)
2176
+ elif jobType == 'alchemical':
2177
+ self._showAlchemicalResults(unit)
2178
+ elif jobType == 'LDDM':
2179
+ self._showLDDMResults(unit)
2180
+
2181
+ return f
2182
+
2183
+ def _generateInputFiles(self):
2184
+ """generate input files for binding free energy simulation
2185
+
2186
+ Returens:
2187
+ function obj: a slot function that generates input files for binding free energy simulation
2188
+ """
2189
+
2190
+ def f():
2191
+ path = QFileDialog.getExistingDirectory(None, 'Select a directory')
2192
+ # cancel
2193
+ if path == '':
2194
+ return
2195
+
2196
+ iGenerator = inputGenerator.inputGenerator()
2197
+
2198
+ # third-party softwares and user-provided solvation boxes
2199
+ for item in [
2200
+ self.mainSettings.vmdLineEdit.text(),
2201
+ #self.mainSettings.gromacsLineEdit.text(),
2202
+ #self.mainSettings.tleapLineEdit.text(),
2203
+ self.geometricAdvancedSettings.nonStandardSolventPdbLineEdit.text(),
2204
+ self.geometricAdvancedSettings.nonStandardSolventPsfLineEdit.text()
2205
+ ]:
2206
+ if ((not os.path.exists(item)) and item != ''):
2207
+ QMessageBox.warning(self, 'Error', f'file {item} does not exist!')
2208
+ return
2209
+
2210
+ if self.preTreatmentMainTabs.currentIndex() == 0:
2211
+ # force fields
2212
+ forceFieldFiles = [self.forceFieldFilesBox.item(i).text() for i in range(self.forceFieldFilesBox.count())]
2213
+
2214
+ # NAMD files
2215
+ for item in [self.psfLineEdit.text(), self.coorLineEdit.text()] + forceFieldFiles:
2216
+ if not os.path.exists(item):
2217
+ QMessageBox.warning(self, 'Error', f'file {item} does not exist!')
2218
+ return
2219
+
2220
+ # check inputs
2221
+ try:
2222
+ float(self.temperatureLineEdit.text())
2223
+ stratification = [
2224
+ int(self.geometricAdvancedSettings.stratificationRMSDBoundLineEdit.text()),
2225
+ int(self.geometricAdvancedSettings.stratificationThetaLineEdit.text()),
2226
+ int(self.geometricAdvancedSettings.stratificationPhiLineEdit.text()),
2227
+ int(self.geometricAdvancedSettings.stratificationPsiLineEdit.text()),
2228
+ int(self.geometricAdvancedSettings.stratificationthetaLineEdit.text()),
2229
+ int(self.geometricAdvancedSettings.stratificationphiLineEdit.text()),
2230
+ int(self.geometricAdvancedSettings.stratificationRLineEdit.text()),
2231
+ int(self.geometricAdvancedSettings.stratificationRMSDUnboundLineEdit.text())
2232
+ ]
2233
+ alchemicalStratification = [
2234
+ int(self.alchemicalAdvancedSettings.boundLigandLineEdit.text()),
2235
+ int(self.alchemicalAdvancedSettings.boundRestraintsLineEdit.text()),
2236
+ int(self.alchemicalAdvancedSettings.unboundLigandLineEdit.text()),
2237
+ int(self.alchemicalAdvancedSettings.unboundRestraintsLineEdit.text())
2238
+ ]
2239
+ except:
2240
+ QMessageBox.warning(self, 'Error', f'Force constant or r* input error!')
2241
+ return
2242
+
2243
+ # job type
2244
+ if self.forceFieldCombobox.currentText() == 'CHARMM':
2245
+ forceFieldType = 'charmm'
2246
+ elif self.forceFieldCombobox.currentText() == 'Amber':
2247
+ forceFieldType = 'amber'
2248
+
2249
+ # make sure there are CHARMM FF files
2250
+ if forceFieldFiles == [] and forceFieldType == 'charmm':
2251
+ QMessageBox.warning(
2252
+ self,
2253
+ 'Error',
2254
+ f'\
2255
+ CHARMM force field files must be specified!'
2256
+ )
2257
+ return
2258
+
2259
+ if self.selectStrategyCombobox.currentText() == 'Geometric':
2260
+
2261
+ if self.selectMDEngineCombobox.currentText().lower() == 'gromacs':
2262
+ QMessageBox.warning(
2263
+ self, 'Warning', f'Please pay attention if you use the new Gromacs interface: \n\
2264
+ 1. Occationally, the atom number in complex.ndx do not correct. If so, just modify it manually; \n\
2265
+ 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\
2266
+ 3. Gromacs patched by the latest (master branch) version of Colvars is needed.'
2267
+ )
2268
+
2269
+ # for the amber force field, files of large box must be provided
2270
+ if forceFieldType == 'amber':
2271
+
2272
+ if self.geometricAdvancedSettings.nonStandardSolventPdbLineEdit.text() == '' or \
2273
+ self.geometricAdvancedSettings.nonStandardSolventPsfLineEdit.text() == '':
2274
+ QMessageBox.warning(
2275
+ self,
2276
+ 'Error',
2277
+ f'\
2278
+ Coordinate and topology files of large box must be \
2279
+ provided in "Advanced Settings"when using the Amber \
2280
+ force fields!'
2281
+ )
2282
+ return
2283
+
2284
+ if self.geometricAdvancedSettings.useGaWTMCheckbox.isChecked():
2285
+
2286
+ if self.geometricAdvancedSettings.useCUDASOAIntegrator.isChecked():
2287
+ QMessageBox.warning(self, 'Error',
2288
+ f'\
2289
+ GaWTM-eABF is not compatible with CUDASOAIntegrator in NAMD! \n'
2290
+ )
2291
+ return
2292
+
2293
+ QMessageBox.warning(self, 'Warning',
2294
+ f'\
2295
+ The feature of using GaWTM-eABF as the workhorse engine is \
2296
+ experimental! Please always use the latest devel version of NAMD!\n'
2297
+ )
2298
+
2299
+ try:
2300
+ iGenerator.generateNAMDGeometricFiles(
2301
+ path,
2302
+ self.psfLineEdit.text(),
2303
+ self.coorLineEdit.text(),
2304
+ forceFieldType,
2305
+ forceFieldFiles,
2306
+ float(self.temperatureLineEdit.text()),
2307
+ self.selectProteineLineEdit.text(),
2308
+ self.selectLigandLineEdit.text(),
2309
+ self.geometricAdvancedSettings.userDefinedDirectionLineEdit.text(),
2310
+ self.geometricAdvancedSettings.nonStandardSolventPsfLineEdit.text(),
2311
+ self.geometricAdvancedSettings.nonStandardSolventPdbLineEdit.text(),
2312
+ stratification,
2313
+ self.geometricAdvancedSettings.memProCheckbox.isChecked(),
2314
+ self.geometricAdvancedSettings.neutralizeLigOnlyCombobox.currentText(),
2315
+ self.geometricAdvancedSettings.pinDownProCheckbox.isChecked(),
2316
+ self.geometricAdvancedSettings.useOldCvCheckbox.isChecked(),
2317
+ int(self.geometricAdvancedSettings.parallelRunsLineEdit.text()),
2318
+ self.mainSettings.vmdLineEdit.text(),
2319
+ self.geometricAdvancedSettings.reflectingBoundaryCheckbox.isChecked(),
2320
+ self.selectMDEngineCombobox.currentText().lower(),
2321
+ self.geometricAdvancedSettings.OPLSMixingRuleCheckbox.isChecked(),
2322
+ self.geometricAdvancedSettings.considerRMSDCVCheckbox.isChecked(),
2323
+ self.geometricAdvancedSettings.useGaWTMCheckbox.isChecked(),
2324
+ self.geometricAdvancedSettings.useCUDASOAIntegrator.isChecked(),
2325
+ float(self.geometricAdvancedSettings.timestepLineEdit.text())
2326
+ )
2327
+ except fileParser.SelectionError:
2328
+ QMessageBox.warning(
2329
+ self,
2330
+ 'Error',
2331
+ f'\
2332
+ Selection corresponding to nothing!\n\
2333
+ Check your selection again!'
2334
+ )
2335
+ if os.path.exists(f'{path}/BFEE'):
2336
+ shutil.rmtree(f'{path}/BFEE')
2337
+ return
2338
+ except inputGenerator.DirectoryExistError:
2339
+ QMessageBox.warning(
2340
+ self,
2341
+ 'Error',
2342
+ f'\
2343
+ ./BFEE* directory already exists!'
2344
+ )
2345
+ return
2346
+ except inputGenerator.FileTypeUnknownError:
2347
+ QMessageBox.warning(
2348
+ self,
2349
+ 'Error',
2350
+ f'\
2351
+ Unkwn input file types! The following file types are supported:\n\
2352
+ psf, parm, prm7, parm7, prmtop, pdb, coor, rst, rst7, inpcrd '
2353
+ )
2354
+ return
2355
+ except PermissionError:
2356
+ QMessageBox.warning(
2357
+ self,
2358
+ 'Error',
2359
+ f'\
2360
+ Cannot read input files due to the permission reason!\n\
2361
+ Restart the program or check the authority of the files!'
2362
+ )
2363
+ return
2364
+ except Exception as e:
2365
+ print(e)
2366
+ QMessageBox.warning(
2367
+ self,
2368
+ 'Error',
2369
+ f'\
2370
+ Unknown error! The error message is: \n\
2371
+ {e}\n'
2372
+ )
2373
+ return
2374
+
2375
+ elif self.selectStrategyCombobox.currentText() == 'Alchemical':
2376
+
2377
+ try:
2378
+ iGenerator.generateNAMDAlchemicalFiles(
2379
+ path,
2380
+ self.psfLineEdit.text(),
2381
+ self.coorLineEdit.text(),
2382
+ forceFieldType,
2383
+ forceFieldFiles,
2384
+ float(self.temperatureLineEdit.text()),
2385
+ self.selectProteineLineEdit.text(),
2386
+ self.selectLigandLineEdit.text(),
2387
+ alchemicalStratification,
2388
+ self.alchemicalAdvancedSettings.doubleWideCheckbox.isChecked(),
2389
+ self.alchemicalAdvancedSettings.minBeforeSampleCheckbox.isChecked(),
2390
+ self.alchemicalAdvancedSettings.memProCheckbox.isChecked(),
2391
+ self.alchemicalAdvancedSettings.neutralizeLigOnlyCombobox.currentText(),
2392
+ self.alchemicalAdvancedSettings.pinDownProCheckbox.isChecked(),
2393
+ self.alchemicalAdvancedSettings.useOldCvCheckbox.isChecked(),
2394
+ self.mainSettings.vmdLineEdit.text(),
2395
+ self.alchemicalAdvancedSettings.OPLSMixingRuleCheckbox.isChecked(),
2396
+ self.alchemicalAdvancedSettings.considerRMSDCVCheckbox.isChecked(),
2397
+ self.alchemicalAdvancedSettings.useCUDASOAIntegrator.isChecked(),
2398
+ float(self.alchemicalAdvancedSettings.timestepLineEdit.text()),
2399
+ self.alchemicalAdvancedSettings.reEqCheckbox.isChecked(),
2400
+ self.alchemicalAdvancedSettings.LDDMCheckbox.isChecked(),
2401
+ self.alchemicalAdvancedSettings.useWTMLambdaABFCheckbox.isChecked()
2402
+ )
2403
+ except PermissionError:
2404
+ QMessageBox.warning(
2405
+ self,
2406
+ 'Error',
2407
+ f'\
2408
+ Cannot read input files due to the permission reason!\n\
2409
+ Restart the program or check the authority of the files!'
2410
+ )
2411
+ return
2412
+ except fileParser.SelectionError:
2413
+ QMessageBox.warning(
2414
+ self,
2415
+ 'Error',
2416
+ f'\
2417
+ Selection corresponding to nothing!\n\
2418
+ Check your selection again!'
2419
+ )
2420
+ if os.path.exists(f'{path}/BFEE'):
2421
+ shutil.rmtree(f'{path}/BFEE')
2422
+ return
2423
+ except inputGenerator.DirectoryExistError:
2424
+ QMessageBox.warning(
2425
+ self,
2426
+ 'Error',
2427
+ f'\
2428
+ ./BFEE directory already exists!'
2429
+ )
2430
+ return
2431
+ except inputGenerator.FileTypeUnknownError:
2432
+ QMessageBox.warning(
2433
+ self,
2434
+ 'Error',
2435
+ f'\
2436
+ Unkwn input file types! The following file types are supported:\n\
2437
+ psf, parm, prm7, parm7, prmtop, pdb, coor, rst, rst7, inpcrd '
2438
+ )
2439
+ return
2440
+ except Exception as e:
2441
+ print(e)
2442
+ QMessageBox.warning(
2443
+ self,
2444
+ 'Error',
2445
+ f'\
2446
+ Unknown error! The error message is: \n\
2447
+ {e}\n'
2448
+ )
2449
+ return
2450
+
2451
+ # gromacs
2452
+ if self.preTreatmentMainTabs.currentIndex() == 1:
2453
+
2454
+ QMessageBox.warning(
2455
+ self, 'Warning', ('<ol>\
2456
+ <li>Any setting in "Advanced settings" is not supported\
2457
+ when using Gromacs-formatted files as inputs!</li>\
2458
+ <li>C-rescale pressure coupling (pcoupl) is used for all simulations, \
2459
+ GROMACS version >= 2021 with Colvars module is required. \
2460
+ You may need to download it from the \
2461
+ <a href=\'https://github.com/Colvars/colvars/\'>Colvars website</a>.</li></ol>'))
2462
+
2463
+ for item in [
2464
+ self.topLineEdit.text(),
2465
+ self.gromacsPdbLineEdit.text(),
2466
+ self.gromacsLigandOnlyPdbLineEdit.text(),
2467
+ self.gromacsLigandOnlyTopLineEdit.text()
2468
+ ]:
2469
+ if not os.path.exists(item):
2470
+ QMessageBox.warning(self, 'Error', f'file {item} does not exist!')
2471
+ return
2472
+
2473
+ if self.selectStrategyCombobox.currentText() == 'Geometric':
2474
+ try:
2475
+ iGenerator.generateGromacsGeometricFiles(
2476
+ path=path,
2477
+ topFile=self.topLineEdit.text(),
2478
+ pdbFile=self.gromacsPdbLineEdit.text(),
2479
+ pdbFileFormat=self.gromacsStructureFileFormatCombobox.currentText(),
2480
+ ligandOnlyTopFile=self.gromacsLigandOnlyTopLineEdit.text(),
2481
+ ligandOnlyPdbFile=self.gromacsLigandOnlyPdbLineEdit.text(),
2482
+ ligandOnlyPdbFileFormat=self.gromacsLigandOnlyStructureFileFormatCombobox.currentText(),
2483
+ selectionPro=self.selectProteineLineEdit.text(),
2484
+ selectionLig=self.selectLigandLineEdit.text(),
2485
+ temperature=float(self.temperatureLineEdit.text())
2486
+ )
2487
+ except inputGenerator.DirectoryExistError:
2488
+ QMessageBox.warning(
2489
+ self,
2490
+ 'Error',
2491
+ f'\
2492
+ ./BFEE directory already exists!'
2493
+ )
2494
+ return
2495
+ except BFEEGromacs.SelectionError:
2496
+ QMessageBox.warning(
2497
+ self,
2498
+ 'Error',
2499
+ f'\
2500
+ Selection corresponding to nothing!\n\
2501
+ Check your selection again!'
2502
+ )
2503
+ if os.path.exists(f'{path}/BFEE'):
2504
+ shutil.rmtree(f'{path}/BFEE')
2505
+ return
2506
+ except Exception as e:
2507
+ print(e)
2508
+ QMessageBox.warning(
2509
+ self,
2510
+ 'Error',
2511
+ f'\
2512
+ Unknown error!'
2513
+ )
2514
+ return
2515
+ elif self.selectStrategyCombobox.currentText() == 'Alchemical':
2516
+ QMessageBox.warning(self, 'Error', f'Alchemical route is not supported using Gromacs!')
2517
+ return
2518
+
2519
+ QMessageBox.information(self, 'Input generation', f'Input files have been generated successfully!')
2520
+
2521
+ del iGenerator
2522
+
2523
+ return f
2524
+
2525
+ def _mergePMFs(self):
2526
+ """merge a series of overlapped pmfs
2527
+
2528
+ Returns:
2529
+ function obj: a slot function that merges a series of overlapped pmfs
2530
+ """
2531
+
2532
+ def f():
2533
+ if self.mergePmfBox.count() == 0:
2534
+ QMessageBox.warning(self, 'Warning', f'Warning, no PMF selected!')
2535
+ return
2536
+
2537
+ path, _ = QFileDialog.getSaveFileName(None, 'Set the name of merged PMF')
2538
+
2539
+ pmfFiles = [self.mergePmfBox.item(i).text() for i in range(self.mergePmfBox.count())]
2540
+
2541
+ if not ploter.isGaWTM(pmfFiles):
2542
+ pmfs = [ploter.readPMF(item) for item in pmfFiles]
2543
+ else:
2544
+ pmfFiles = [item for item in pmfFiles if not item.endswith('.reweightamd1.cumulant.pmf')]
2545
+ try:
2546
+ pmfs = [ploter.correctGaWTM(item) for item in pmfFiles]
2547
+ except ploter.NoCorrectionFileError:
2548
+ QMessageBox.warning(self, 'Error', f'A PMF file does not have corresponding correction!')
2549
+ return
2550
+
2551
+ mergedPMF = ploter.mergePMF(pmfs)
2552
+ ploter.writePMF(path, mergedPMF)
2553
+ QMessageBox.information(self, 'Merge PMFs', f'PMF merged successfully!')
2554
+ return f
2555
+
2556
+ def _plotPMFs(self):
2557
+ """plot a series of overlapped pmfs
2558
+
2559
+ Returns:
2560
+ function obj: a slot function that plots a series of overlapped pmfs
2561
+ """
2562
+
2563
+ def f():
2564
+ if self.plotPmfBox.count() == 0:
2565
+ QMessageBox.warning(self, 'Warning', f'Warning, no PMF selected!')
2566
+ return
2567
+
2568
+ pmfFiles = [self.plotPmfBox.item(i).text() for i in range(self.plotPmfBox.count())]
2569
+
2570
+ if not ploter.isGaWTM(pmfFiles):
2571
+ pmfs = [ploter.readPMF(item) for item in pmfFiles]
2572
+ else:
2573
+ pmfFiles = [item for item in pmfFiles if not item.endswith('.reweightamd1.cumulant.pmf')]
2574
+ try:
2575
+ pmfs = [ploter.correctGaWTM(item) for item in pmfFiles]
2576
+ except ploter.NoCorrectionFileError:
2577
+ QMessageBox.warning(self, 'Error', f'A PMF file does not have corresponding correction!')
2578
+ return
2579
+
2580
+ mergedPMF = ploter.mergePMF(pmfs)
2581
+ ploter.plotPMF(mergedPMF)
2582
+ return f
2583
+
2584
+ def _plotRMSDConvergence(self):
2585
+ """plot time evolution of PMF rmsd with respect to zero array
2586
+
2587
+ Returns:
2588
+ function obj: a slot function that plots time evolution of PMF rmsd with respect to zero array
2589
+ """
2590
+
2591
+ def f():
2592
+ path = self.plotPmfConvergenceBox.text()
2593
+ if not os.path.exists(path):
2594
+ QMessageBox.warning(self, 'Error', f'file {path} does not exist!')
2595
+ return
2596
+
2597
+ rmsds = ploter.parseHistFile(path)
2598
+ ploter.plotConvergence(rmsds)
2599
+ return f
2600
+
2601
+ def _plotHysteresis(self):
2602
+ """plot hysteresis between forward and backward alchemical transformations
2603
+
2604
+ Returns:
2605
+ function obj: a slot function that plot hysteresis between forward and backward alchemical transformations
2606
+ """
2607
+
2608
+ def f():
2609
+ forwardFilePath = self.plotHysteresisForwardLineEdit.text()
2610
+ backwardFilePath = self.plotHysteresisBackwardLineEdit.text()
2611
+ if not os.path.exists(forwardFilePath):
2612
+ QMessageBox.warning(self, 'Error', f'file {forwardFilePath} does not exist!')
2613
+ return
2614
+ if not os.path.exists(backwardFilePath):
2615
+ QMessageBox.warning(self, 'Error', f'file {backwardFilePath} does not exist!')
2616
+ return
2617
+
2618
+ forwardPostfix = os.path.splitext(forwardFilePath)[-1]
2619
+ backwardPostfix = os.path.splitext(backwardFilePath)[-1]
2620
+
2621
+ if forwardPostfix != '.fepout' and forwardPostfix != '.log' \
2622
+ and backwardPostfix != '.fepout' and forwardPostfix != '.log':
2623
+ QMessageBox.warning(self, 'Error', f'File type not correct!')
2624
+ return
2625
+
2626
+ if forwardPostfix != backwardPostfix:
2627
+ QMessageBox.warning(self, 'Error', f'File types of forward and backward simulations are not the same!')
2628
+ return
2629
+
2630
+ pTreat = postTreatment.postTreatment(float(self.alchemicalPostTemperatureLineEdit.text()), 'namd', 'alchemical')
2631
+ if forwardPostfix == '.fepout':
2632
+ forwardProfile = np.transpose(pTreat._fepoutFile(forwardFilePath))
2633
+ backwardProfile = np.transpose(pTreat._fepoutFile(backwardFilePath))
2634
+ backwardProfile[:,1] *= -1
2635
+ elif forwardPostfix == '.log':
2636
+ forwardProfile = np.transpose(pTreat._tiLogFile(forwardFilePath, self.isRigidLigandCheckbox.isChecked()))
2637
+ backwardProfile = np.transpose(pTreat._tiLogFile(backwardFilePath, self.isRigidLigandCheckbox.isChecked()))
2638
+ ploter.plotHysteresis(forwardProfile, backwardProfile)
2639
+ return f
2640
+
2641
+ def _quickSetProteinProteinGeometric(self):
2642
+ """quick setting for protein-protein binding free energy calculations through the geometrical route
2643
+ """
2644
+
2645
+ self.selectMDEngineCombobox.setCurrentText("NAMD")
2646
+ self.selectStrategyCombobox.setCurrentText("Geometric")
2647
+ self.geometricAdvancedSettings.stratificationRLineEdit.setText("5")
2648
+ self.geometricAdvancedSettings.useCUDASOAIntegrator.setChecked(False)
2649
+ self.geometricAdvancedSettings.considerRMSDCVCheckbox.setChecked(False)
2650
+ self.geometricAdvancedSettings.useGaWTMCheckbox.setChecked(True)
2651
+ QMessageBox.information(self, 'Settings', f'Changed settings for protein-protein binding free-energy calculations \
2652
+ through the geometrical route!')
2653
+
2654
+ def _quickSetProteinLigandGeometric(self):
2655
+ """quick setting for protein-ligand binding free energy calculations through the geometrical route
2656
+ """
2657
+
2658
+ self.selectMDEngineCombobox.setCurrentText("NAMD")
2659
+ self.selectStrategyCombobox.setCurrentText("Geometric")
2660
+ self.geometricAdvancedSettings.stratificationRMSDBoundLineEdit.setText("3")
2661
+ self.geometricAdvancedSettings.stratificationRMSDUnboundLineEdit.setText("3")
2662
+ self.geometricAdvancedSettings.stratificationRLineEdit.setText("5")
2663
+ self.geometricAdvancedSettings.useCUDASOAIntegrator.setChecked(True)
2664
+ self.geometricAdvancedSettings.considerRMSDCVCheckbox.setChecked(True)
2665
+ self.geometricAdvancedSettings.useGaWTMCheckbox.setChecked(False)
2666
+ QMessageBox.information(self, 'Settings', f'Changed settings for protein-ligand binding free-energy calculations \
2667
+ through the geometrical route!')
2668
+
2669
+ def _quickSetProteinLigandAlchemical(self):
2670
+ """quick setting for protein-ligand binding free energy calculations through the alchemical route
2671
+ """
2672
+
2673
+ self.selectMDEngineCombobox.setCurrentText("NAMD")
2674
+ self.selectStrategyCombobox.setCurrentText("Alchemical")
2675
+ self.alchemicalAdvancedSettings.boundLigandLineEdit.setText("200")
2676
+ self.alchemicalAdvancedSettings.boundRestraintsLineEdit.setText("200")
2677
+ self.alchemicalAdvancedSettings.unboundLigandLineEdit.setText("100")
2678
+ self.alchemicalAdvancedSettings.unboundRestraintsLineEdit.setText("100")
2679
+ self.alchemicalAdvancedSettings.doubleWideCheckbox.setChecked(True)
2680
+ self.alchemicalAdvancedSettings.useCUDASOAIntegrator.setChecked(True)
2681
+ self.alchemicalAdvancedSettings.reEqCheckbox.setChecked(True)
2682
+ self.alchemicalAdvancedSettings.LDDMCheckbox.setChecked(False)
2683
+ self.alchemicalAdvancedSettings.useWTMLambdaABFCheckbox.setChecked(False)
2684
+ self.alchemicalAdvancedSettings.considerRMSDCVCheckbox.setChecked(True)
2685
+ QMessageBox.information(self, 'Settings', f'Changed settings for protein-ligand binding free-energy calculations \
2686
+ through the alchemical route!')
2687
+
2688
+ def _quickSetProteinLigandLDDM(self):
2689
+ """quick setting for protein-ligand binding free energy calculations through the alchemical route
2690
+ """
2691
+
2692
+ self.selectMDEngineCombobox.setCurrentText("NAMD")
2693
+ self.selectStrategyCombobox.setCurrentText("Alchemical")
2694
+
2695
+ self.alchemicalAdvancedSettings.pinDownProCheckbox.setChecked(True)
2696
+ self.alchemicalAdvancedSettings.useCUDASOAIntegrator.setChecked(True)
2697
+
2698
+ self.alchemicalAdvancedSettings.LDDMCheckbox.setChecked(True)
2699
+
2700
+ self.alchemicalAdvancedSettings.boundLigandLineEdit.setText('200')
2701
+ self.alchemicalAdvancedSettings.unboundLigandLineEdit.setText('100')
2702
+
2703
+ self.alchemicalAdvancedSettings.timestepLineEdit.setText('2.0')
2704
+ QMessageBox.information(self, 'Settings', f'Changed settings for protein-ligand binding free-energy calculations \
2705
+ through the LDDM!')
2706
+
2707
+ def _quickSetAI(self):
2708
+ """AI-assisted setting for binding free energy calculations
2709
+ """
2710
+ if self.mainSettings.openRouterAPILineEdit.text() == '' or self.mainSettings.openRouterModelLineEdit.text() == '':
2711
+ QMessageBox.warning(self, 'Warning', 'Please set both the OpenRouter API and model paths before using the AI assistance.')
2712
+ return
2713
+ self.aiAssistantDialog.show()
2714
+
2715
+ def _initSingalsSlots(self):
2716
+ """initialize (connect) singals and slots
2717
+ """
2718
+
2719
+ # pre-treatment tab
2720
+ self.selectStrategyAdvancedButton.clicked.connect(self._advancedSettings(self.selectStrategyCombobox))
2721
+ self.preTreatmentMainTabs.currentChanged.connect(self._changeStrategySettingStateForOldGromacs)
2722
+
2723
+ # NAMD tab
2724
+ self.psfButton.clicked.connect(commonSlots.openFileDialog('psf/parm', self.psfLineEdit))
2725
+ self.coorButton.clicked.connect(commonSlots.openFileDialog('pdb/rst', self.coorLineEdit))
2726
+
2727
+ # force field selection
2728
+ self.forceFieldCombobox.currentTextChanged.connect(self._changeFFButtonState)
2729
+ self.forceFieldAddButton.clicked.connect(commonSlots.openFilesDialog('prm', self.forceFieldFilesBox))
2730
+ self.forceFieldClearButton.clicked.connect(self.forceFieldFilesBox.clear)
2731
+
2732
+ # MD engine
2733
+ self.selectMDEngineCombobox.currentTextChanged.connect(self._changeStrategySettingState)
2734
+
2735
+ # gromacs tab
2736
+ self.gromacsPdbButton.clicked.connect(commonSlots.openFileDialog('pdb', self.gromacsPdbLineEdit))
2737
+ self.topButton.clicked.connect(commonSlots.openFileDialog('top', self.topLineEdit))
2738
+ self.gromacsLigandOnlyPdbButton.clicked.connect(commonSlots.openFileDialog('pdb', self.gromacsLigandOnlyPdbLineEdit))
2739
+ self.gromacsLigandOnlyTopButton.clicked.connect(commonSlots.openFileDialog('top', self.gromacsLigandOnlyTopLineEdit))
2740
+
2741
+ # geometric tab
2742
+ self.rmsdBoundButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.rmsdBoundLineEdit))
2743
+ self.rmsdUnboundButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.rmsdUnboundLineEdit))
2744
+ self.ThetaButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.ThetaLineEdit))
2745
+ self.PhiButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.PhiLineEdit))
2746
+ self.PsiButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.PsiLineEdit))
2747
+ self.thetaButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.thetaLineEdit))
2748
+ self.phiButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.phiLineEdit))
2749
+ self.rButton.clicked.connect(commonSlots.openFileDialog('czar.pmf/UI.pmf', self.rLineEdit))
2750
+
2751
+ # alchemical tab
2752
+ self.alchemicalForwardButton1.clicked.connect(commonSlots.openFileDialog('log', self.alchemicalForwardLineEdit1))
2753
+ self.alchemicalBackwardButton1.clicked.connect(commonSlots.openFileDialog('log', self.alchemicalBackwardLineEdit1))
2754
+ self.alchemicalForwardButton2.clicked.connect(commonSlots.openFileDialog('log', self.alchemicalForwardLineEdit2))
2755
+ self.alchemicalBackwardButton2.clicked.connect(commonSlots.openFileDialog('log', self.alchemicalBackwardLineEdit2))
2756
+ self.alchemicalForwardButton3.clicked.connect(commonSlots.openFileDialog('fepout', self.alchemicalForwardLineEdit3))
2757
+ self.alchemicalBackwardButton3.clicked.connect(commonSlots.openFileDialog('fepout', self.alchemicalBackwardLineEdit3))
2758
+ self.alchemicalForwardButton4.clicked.connect(commonSlots.openFileDialog('fepout', self.alchemicalForwardLineEdit4))
2759
+ self.alchemicalBackwardButton4.clicked.connect(commonSlots.openFileDialog('fepout', self.alchemicalBackwardLineEdit4))
2760
+
2761
+ # LDDM tab
2762
+ self.LDDMStep1ColvarsButton.clicked.connect(commonSlots.openFileDialog('in.tmp', self.LDDMStep1ColvarsLineEdit))
2763
+ self.LDDMStep1ColvarsTrajButton.clicked.connect(commonSlots.openFileDialog('colvars.traj', self.LDDMStep1ColvarsTrajLineEdit))
2764
+ self.LDDMStep1FepoutButton.clicked.connect(commonSlots.openFileDialog('fepout', self.LDDMStep1FepoutLineEdit))
2765
+ self.LDDMStep3FepoutButton.clicked.connect(commonSlots.openFileDialog('in.tmp', self.LDDMStep3FepoutLineEdit))
2766
+
2767
+ # generate input files
2768
+ self.generateInputButton.clicked.connect(self._generateInputFiles())
2769
+
2770
+ # calculate binding free energy
2771
+ self.calculateButton.clicked.connect(self._showFinalResults())
2772
+
2773
+ # quick-plot tab
2774
+ self.plotPmfAddButton.clicked.connect(commonSlots.openFilesDialog('pmf', self.plotPmfBox))
2775
+ self.plotPmfClearButton.clicked.connect(self.plotPmfBox.clear)
2776
+ self.plotPmfPlotButton.clicked.connect(self._plotPMFs())
2777
+ self.mergePmfAddButton.clicked.connect(commonSlots.openFilesDialog('pmf', self.mergePmfBox))
2778
+ self.mergePmfClearButton.clicked.connect(self.mergePmfBox.clear)
2779
+ self.mergePmfmergeButton.clicked.connect(self._mergePMFs())
2780
+ self.plotPmfConvergenceBrowseButton.clicked.connect(commonSlots.openFileDialog('pmf', self.plotPmfConvergenceBox))
2781
+ self.plotPmfConvergencePlotButton.clicked.connect(self._plotRMSDConvergence())
2782
+ self.plotHysteresisForwardButton.clicked.connect(commonSlots.openFileDialog('fepout/log', self.plotHysteresisForwardLineEdit))
2783
+ self.plotHysteresisBackwardButton.clicked.connect(commonSlots.openFileDialog('fepout/log', self.plotHysteresisBackwardLineEdit))
2784
+ self.plotHysteresisPlotButton.clicked.connect(self._plotHysteresis())
2785
+