bfee2 3.1.1.post1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. BFEE2/__init__.py +0 -0
  2. BFEE2/commonTools/__init__.py +0 -0
  3. BFEE2/commonTools/commonSlots.py +48 -0
  4. BFEE2/commonTools/fileParser.py +327 -0
  5. BFEE2/commonTools/ploter.py +218 -0
  6. BFEE2/doc/Doc.pdf +0 -0
  7. BFEE2/doc/__init__.py +1 -0
  8. BFEE2/gui.py +2785 -0
  9. BFEE2/inputGenerator.py +2949 -0
  10. BFEE2/postTreatment.py +676 -0
  11. BFEE2/templates_gromacs/000.colvars.template +37 -0
  12. BFEE2/templates_gromacs/000.generate_tpr_sh.template +31 -0
  13. BFEE2/templates_gromacs/000.mdp.template +74 -0
  14. BFEE2/templates_gromacs/001.colvars.template +76 -0
  15. BFEE2/templates_gromacs/001.generate_tpr_sh.template +31 -0
  16. BFEE2/templates_gromacs/001.mdp.template +73 -0
  17. BFEE2/templates_gromacs/001.readme.template +1 -0
  18. BFEE2/templates_gromacs/002.colvars.template +101 -0
  19. BFEE2/templates_gromacs/002.generate_tpr_sh.template +31 -0
  20. BFEE2/templates_gromacs/002.mdp.template +73 -0
  21. BFEE2/templates_gromacs/003.colvars.template +125 -0
  22. BFEE2/templates_gromacs/003.generate_tpr_sh.template +36 -0
  23. BFEE2/templates_gromacs/003.mdp.template +73 -0
  24. BFEE2/templates_gromacs/004.colvars.template +148 -0
  25. BFEE2/templates_gromacs/004.generate_tpr_sh.template +37 -0
  26. BFEE2/templates_gromacs/004.mdp.template +74 -0
  27. BFEE2/templates_gromacs/005.colvars.template +170 -0
  28. BFEE2/templates_gromacs/005.generate_tpr_sh.template +38 -0
  29. BFEE2/templates_gromacs/005.mdp.template +74 -0
  30. BFEE2/templates_gromacs/006.colvars.template +192 -0
  31. BFEE2/templates_gromacs/006.generate_tpr_sh.template +39 -0
  32. BFEE2/templates_gromacs/006.mdp.template +74 -0
  33. BFEE2/templates_gromacs/007.colvars.template +210 -0
  34. BFEE2/templates_gromacs/007.generate_tpr_sh.template +40 -0
  35. BFEE2/templates_gromacs/007.mdp.template +73 -0
  36. BFEE2/templates_gromacs/007_eq.colvars.template +169 -0
  37. BFEE2/templates_gromacs/007_eq.generate_tpr_sh.template +64 -0
  38. BFEE2/templates_gromacs/007_min.mdp.template +62 -0
  39. BFEE2/templates_gromacs/008.colvars.template +42 -0
  40. BFEE2/templates_gromacs/008.generate_tpr_sh.template +31 -0
  41. BFEE2/templates_gromacs/008.mdp.template +74 -0
  42. BFEE2/templates_gromacs/008_eq.colvars.template +14 -0
  43. BFEE2/templates_gromacs/008_eq.generate_tpr_sh.template +31 -0
  44. BFEE2/templates_gromacs/BFEEGromacs.py +1268 -0
  45. BFEE2/templates_gromacs/__init__.py +0 -0
  46. BFEE2/templates_gromacs/find_min_max.awk +27 -0
  47. BFEE2/templates_namd/__init__.py +0 -0
  48. BFEE2/templates_namd/configTemplate.py +1152 -0
  49. BFEE2/templates_namd/fep.tcl +299 -0
  50. BFEE2/templates_namd/fep_lddm.tcl +312 -0
  51. BFEE2/templates_namd/scriptTemplate.py +304 -0
  52. BFEE2/templates_namd/solvate.tcl +9 -0
  53. BFEE2/templates_namd/solvate_mem.tcl +9 -0
  54. BFEE2/templates_namd/updateCenters.py +312 -0
  55. BFEE2/templates_readme/Readme_Gromacs_Geometrical.txt +25 -0
  56. BFEE2/templates_readme/Readme_NAMD_Alchemical.txt +20 -0
  57. BFEE2/templates_readme/Readme_NAMD_Geometrical.txt +34 -0
  58. BFEE2/templates_readme/__init__.py +1 -0
  59. BFEE2/templates_readme/rags.py +187 -0
  60. BFEE2/third_party/__init__.py +0 -0
  61. BFEE2/third_party/py_bar.py +585 -0
  62. BFEE2/version.py +4 -0
  63. bfee2-3.1.1.post1.data/scripts/BFEE2Gui.py +19 -0
  64. bfee2-3.1.1.post1.dist-info/METADATA +86 -0
  65. bfee2-3.1.1.post1.dist-info/RECORD +68 -0
  66. bfee2-3.1.1.post1.dist-info/WHEEL +5 -0
  67. bfee2-3.1.1.post1.dist-info/licenses/LICENSE +677 -0
  68. bfee2-3.1.1.post1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1268 @@
1
+ #!/usr/bin/env python3
2
+ import numpy as np
3
+ import os
4
+ import sys
5
+ import posixpath
6
+ import string
7
+ import logging
8
+ import shutil
9
+ from MDAnalysis import Universe
10
+ from MDAnalysis.units import convert
11
+ from MDAnalysis import transformations
12
+ from math import isclose
13
+
14
+ try:
15
+ import importlib.resources as pkg_resources
16
+ except ImportError:
17
+ # Try backported to PY<37 `importlib_resources`.
18
+ import importlib_resources as pkg_resources
19
+
20
+ try:
21
+ from BFEE2 import templates_gromacs
22
+ except ImportError:
23
+ templates_gromacs = __package__
24
+
25
+ # an runtime error
26
+ # selection corresponding to nothing
27
+ class SelectionError(RuntimeError):
28
+ def __init__(self, arg):
29
+ self.args = arg
30
+
31
+ def scanGromacsTopologyInclude(gmxTopFile, logger=None):
32
+ """scan the files included by a gromacs topology file
33
+
34
+ Args:
35
+ gmxTopFile (str): filename of the gromacs topology file
36
+ logger (Logging.logger, optional): logger for debugging. Defaults to None.
37
+
38
+ Returns:
39
+ tuple:
40
+ tuple:
41
+ a (list, list) tuple.
42
+ The first list contains the absolute pathnames of included files.
43
+ The second list contains the strings in the quotation marks for handling
44
+ relative paths.
45
+ Logging.logger:
46
+ logger for debugging
47
+ """
48
+
49
+ topology_dirpath = os.path.dirname(os.path.abspath(gmxTopFile))
50
+ include_files = []
51
+ include_strings = []
52
+ with open(gmxTopFile, 'r') as finput:
53
+ for line in finput:
54
+ line = line.strip()
55
+ if line.startswith('#include'):
56
+ # this is an include line
57
+ # find the first quote
58
+ first_quote = line.find('"')
59
+ # find the second quote
60
+ second_quote = line.find('"', first_quote+1)
61
+ # save the string of include
62
+ include_str = line[first_quote+1:second_quote]
63
+ # find the absolute path and save it to the list
64
+ include_filename = os.path.join(topology_dirpath, include_str).replace('\\', '/')
65
+ if (posixpath.exists(include_filename)):
66
+ include_files.append(include_filename)
67
+ include_strings.append(include_str)
68
+ else:
69
+ if logger is None:
70
+ print(f'Warning: {include_filename} does not exists.')
71
+ else:
72
+ logger.warning(f'{include_filename} does not exists.')
73
+ return include_files, include_strings
74
+
75
+
76
+ def measure_minmax(atom_positions):
77
+ """mimic the VMD command "measure minmax"
78
+
79
+ Args:
80
+ atom_positions (numpy.array): a numpy array containing the XYZ coordinates of N atoms. The shape
81
+ should be (N, 3).
82
+
83
+ Returns:
84
+ Numpy.array: a shape of (2, 3) array, where the first subarray has the minimum
85
+ values in XYZ directions, and the second subarray has the maximum
86
+ values in XYZ directions.
87
+ """
88
+
89
+ xyz_array = np.transpose(atom_positions)
90
+ min_x = np.min(xyz_array[0])
91
+ max_x = np.max(xyz_array[0])
92
+ min_y = np.min(xyz_array[1])
93
+ max_y = np.max(xyz_array[1])
94
+ min_z = np.min(xyz_array[2])
95
+ max_z = np.max(xyz_array[2])
96
+ return np.array([[min_x, min_y, min_z],[max_x, max_y, max_z]])
97
+
98
+
99
+ def measure_center(atom_positions):
100
+ """mimic the VMD command "measure center"
101
+
102
+ Args:
103
+ atom_positions (numpy.array): a numpy array containing the XYZ coordinates of N atoms. The shape should be (N, 3).
104
+
105
+ Returns:
106
+ Numpy.array: a shape of (3,) array contains the geometric center
107
+ """
108
+
109
+ xyz_array = np.transpose(atom_positions)
110
+ center_x = np.average(xyz_array[0])
111
+ center_y = np.average(xyz_array[1])
112
+ center_z = np.average(xyz_array[2])
113
+ return np.array([center_x, center_y, center_z])
114
+
115
+
116
+ def get_cell(atom_positions):
117
+ """mimic the VMD script get_cell.tcl to calculate the cell units
118
+
119
+ Args:
120
+ atom_positions (numpy.array): a numpy array containing the XYZ coordinates of N atoms. The shape should be (N, 3).
121
+
122
+ Returns:
123
+ Numpy.array: a shape of (3,3) array contains periodic cell information
124
+ """
125
+
126
+ minmax_array = measure_minmax(atom_positions)
127
+ vec = minmax_array[1] - minmax_array[0]
128
+ cell_basis_vector1 = np.array([vec[0], 0, 0])
129
+ cell_basis_vector2 = np.array([0, vec[1], 0])
130
+ cell_basis_vector3 = np.array([0, 0, vec[2]])
131
+ return np.array([cell_basis_vector1,
132
+ cell_basis_vector2,
133
+ cell_basis_vector3])
134
+
135
+
136
+ def generateMDP(MDPTemplate, outputPrefix, timeStep, numSteps, temperature, pressure, colvarsFile, logger=None):
137
+ """generate a GROMACS mdp file from a template
138
+
139
+ Args:
140
+ MDPTemplate (str): template MDP file with $dt and $nsteps
141
+ outputPrefix (str): prefix (no .mdp extension) of the output MDP file
142
+ timeStep (float): timestep for running the simulation
143
+ numSteps (int): number of steps for running the simulation
144
+ temperature (float): simulation temperature
145
+ pressure (float): simulation pressure
146
+ logger (Logging.logger, optional): logger for debugging. Defaults to None.
147
+ """
148
+
149
+ #if logger is None:
150
+ #print(f'generateMDP: Generating {outputPrefix + ".mdp"} from template {MDPTemplate}...')
151
+ #print(f'Timestep (dt): {timeStep}')
152
+ #print(f'Number of simulation steps (nsteps): {numSteps}')
153
+ #print(f'Temperature: {temperature}')
154
+ #print(f'Pressure: {pressure}')
155
+ #else:
156
+ #logger.info(f'generateMDP: Generating {outputPrefix + ".mdp"} from template {MDPTemplate}...')
157
+ #logger.info(f'Timestep (dt): {timeStep}')
158
+ #logger.info(f'Number of simulation steps (nsteps): {numSteps}')
159
+ #logger.info(f'Temperature: {temperature}')
160
+ #logger.info(f'Pressure: {pressure}')
161
+ #with open(MDPTemplate, 'r', newline='\n') as finput:
162
+ MDP_content = string.Template(MDPTemplate)
163
+ MDP_content = MDP_content.safe_substitute(dt=timeStep,
164
+ nsteps=numSteps,
165
+ temperature=temperature,
166
+ pressure=pressure,
167
+ colvarsFile=colvarsFile)
168
+ with open(outputPrefix + '.mdp', 'w', newline='\n') as foutput:
169
+ foutput.write(MDP_content)
170
+
171
+ def generateColvars(colvarsTemplate, outputPrefix, logger=None, **kwargs):
172
+ """generate a Colvars configuration file from a template suffixed with '.dat'
173
+
174
+ Args:
175
+ colvarsTemplate (str): path to a colvars template
176
+ outputPrefix (str): (no .dat extension) of the output Colvars configuration file
177
+ logger (Logging.logger, optional): logger for debugging. Defaults to None.
178
+ **kwargs: additional arguments passed to safe_substitute()
179
+ """
180
+
181
+ #if logger is None:
182
+ #print(f'generateColvars: Generating {outputPrefix + ".dat"} from template {colvarsTemplate}...')
183
+ #print('Colvars parameters:')
184
+ #else:
185
+ #logger.info(f'generateColvars: Generating {outputPrefix + ".dat"} from template {colvarsTemplate}...')
186
+ #logger.info('Colvars parameters:')
187
+ for key, val in kwargs.items():
188
+ if logger is None:
189
+ print(f'{key} = {val}')
190
+ else:
191
+ logger.info(f'{key} = {val}')
192
+ #with open(colvarsTemplate, 'r', newline='\n') as finput:
193
+ content = string.Template(colvarsTemplate)
194
+ content = content.safe_substitute(**kwargs)
195
+ with open(outputPrefix + '.dat', 'w', newline='\n') as foutput:
196
+ foutput.write(content)
197
+
198
+ def generateShellScript(shellTemplate, outputPrefix, logger=None, **kwargs):
199
+ """generate a shell script from a template
200
+
201
+ Args:
202
+ shellTemplate (str): path to a shell template
203
+ outputPrefix (str): prefix (no .sh extension) of the output Colvars configuration file
204
+ logger (Logging.logger, optional): logger for debugging. Defaults to None.
205
+ **kwargs: additional arguments passed to safe_substitute()
206
+ """
207
+
208
+ #if logger is None:
209
+ #print(f'generateShellScript: Generating {outputPrefix + ".sh"} from template {shellTemplate}...')
210
+ #else:
211
+ #logger.info(f'generateShellScript: Generating {outputPrefix + ".sh"} from template {shellTemplate}...')
212
+ #with open(shellTemplate, 'r', newline='\n') as finput:
213
+ content = string.Template(shellTemplate)
214
+ content = content.safe_substitute(**kwargs)
215
+ with open(outputPrefix + '.sh', 'w', newline='\n') as foutput:
216
+ foutput.write(content)
217
+
218
+
219
+ def mearsurePolarAngles(proteinCenter, ligandCenter):
220
+ """measure the polar angles between the protein and the ligand
221
+
222
+ Args:
223
+ proteinCenter (numpy.array): center-of-mass of the protein
224
+ ligandCenter (numpy.array): center-of-mass of the ligand
225
+
226
+ Returns:
227
+ tuple: a (theta, phi) tuple where theta and phi are measured in degrees
228
+ """
229
+
230
+ vector = ligandCenter - proteinCenter
231
+ vector /= np.linalg.norm(vector)
232
+ return (np.degrees(np.arccos(vector[2])),
233
+ np.degrees(np.arctan2(vector[1], vector[0])))
234
+
235
+
236
+ class BFEEGromacs:
237
+ """The entry class for handling gromacs inputs in BFEE.
238
+
239
+ Attributes:
240
+ logger (logging.Logger): logger object for debugging
241
+ handler (logging.StreamHandler): output stream of the debug output
242
+ baseDirectory (str): output directory of the generated files
243
+ structureFile (str): filename of the structure file (either in PDB or GRO format) of the
244
+ protein-ligand binding complex
245
+ topologyFile (str): filename of the GROMACS topology file of the protein-ligand binding
246
+ complex
247
+ ligandOnlyStructureFile (str): filename of the structure file (either in PDB or GRO format) of the
248
+ ligand-only system
249
+ system (MDAnalysis.core.universe): MDAnalysis universe of the protein-ligand binding system
250
+ ligandOnlySystem (MDAnalysis.core.universe): MDAnalysis universe of the ligand-only system
251
+ basenames (str): subdirectory names of all eight steps
252
+ ligand (MDAnalysis.core.groups.AtomGroup): selected HEAVY ATOMS of the ligand in the protein-ligand binding
253
+ complex. This attribute does not exist until the call of
254
+ setLigandHeavyAtomsGroup.
255
+ ligandOnly (MDAnalysis.core.groups.AtomGroup): selected HEAVY ATOMS of the ligand in the ligand-only system.
256
+ This attribute does not exist until the call of
257
+ setLigandHeavyAtomsGroup.
258
+ protein (MDAnalysis.core.groups.AtomGroup): selected HEAVY ATOMS of the protein in the protein-ligand binding
259
+ complex. This attribute does not exist until the call of
260
+ setProteinHeavyAtomsGroup.
261
+ solvent (MDAnalysis.core.groups.AtomGroup): selected atoms of the solvents in the protein-ligand binding complex.
262
+ This attribute does not exist until the call of setSolventAtomsGroup.
263
+ temperature (float): the temperature of simulations (default : 300.0)
264
+ """
265
+
266
+ def __init__(self, structureFile, topologyFile, ligandOnlyStructureFile, ligandOnlyTopologyFile, baseDirectory=None, structureFormat='pdb', ligandOnlyStructureFileFormat='pdb'):
267
+ # setup the logger
268
+ self.logger = logging.getLogger()
269
+ self.logger.handlers.clear()
270
+ self.handler = logging.StreamHandler(sys.stdout)
271
+ self.handler.setFormatter(logging.Formatter('%(asctime)s [BFEEGromacs][%(levelname)s]:%(message)s'))
272
+ self.logger.addHandler(self.handler)
273
+ self.logger.setLevel(logging.INFO)
274
+ self.logger.info('Initializing BFEEGromacs...')
275
+ # setup class attributes
276
+ self.structureFile = structureFile
277
+ self.topologyFile = topologyFile
278
+ if baseDirectory is None:
279
+ self.baseDirectory = os.getcwd()
280
+ else:
281
+ self.baseDirectory = baseDirectory
282
+ self.ligandOnlyStructureFile = ligandOnlyStructureFile
283
+ self.ligandOnlyTopologyFile = ligandOnlyTopologyFile
284
+
285
+ # start to load data into MDAnalysis
286
+ self.logger.info(f'Calling MDAnalysis to load structure {self.structureFile} (format {structureFormat}).')
287
+ self.system = Universe(self.structureFile, format=structureFormat)
288
+ self.ligandOnlySystem = Universe(self.ligandOnlyStructureFile, format=structureFormat)
289
+
290
+ # some PDB files do not have cell info
291
+ # so we reset the cell by an estimation
292
+ all_atoms = self.system.select_atoms("all")
293
+ all_atoms_ligandOnly = self.ligandOnlySystem.select_atoms("all")
294
+ self.system.trajectory[0].triclinic_dimensions = get_cell(all_atoms.positions)
295
+ self.ligandOnlySystem.trajectory[0].triclinic_dimensions = get_cell(all_atoms_ligandOnly.positions)
296
+ dim = self.system.dimensions
297
+ ligandOnly_dim = self.ligandOnlySystem.dimensions
298
+ # measure the cell
299
+ volume = dim[0] * dim[1] * dim[2]
300
+ self.logger.info(f'The volume of the simulation box is {volume} Å^3.')
301
+
302
+ # set default temperature to 300.0 K
303
+ self.temperature = 300.0
304
+ self.logger.info(f'You have specified a new base directory at {self.baseDirectory}')
305
+ if not posixpath.exists(self.baseDirectory):
306
+ os.makedirs(self.baseDirectory)
307
+ if not posixpath.exists(posixpath.join(self.baseDirectory, 'Protein')):
308
+ os.makedirs(posixpath.join(self.baseDirectory, 'Protein'))
309
+ if not posixpath.exists(posixpath.join(self.baseDirectory, 'Ligand')):
310
+ os.makedirs(posixpath.join(self.baseDirectory, 'Ligand'))
311
+
312
+ # check if the topologies have other itp files included
313
+ topologyIncludeFiles, topologyIncludeStrings = scanGromacsTopologyInclude(self.topologyFile)
314
+ for includeFile, includeString in zip(topologyIncludeFiles, topologyIncludeStrings):
315
+ # handle something like "#include "toppar/xxx.itp""
316
+ # in this case we need to create the directory named "toppar" in the base directory
317
+ dest_dirname = posixpath.dirname(includeString)
318
+ if dest_dirname:
319
+ # if dest_dirname is not empty
320
+ if not posixpath.exists(posixpath.join(self.baseDirectory, 'Protein', dest_dirname)):
321
+ # if the destination directory does not exist
322
+ os.makedirs(posixpath.join(self.baseDirectory, 'Protein', dest_dirname))
323
+ shutil.copy(includeFile, posixpath.join(self.baseDirectory, 'Protein', dest_dirname))
324
+ # do the same thing to the ligand topology
325
+ topologyIncludeFiles, topologyIncludeStrings = scanGromacsTopologyInclude(self.ligandOnlyTopologyFile)
326
+ for includeFile, includeString in zip(topologyIncludeFiles, topologyIncludeStrings):
327
+ # handle something like "#include "toppar/xxx.itp""
328
+ # in this case we need to create the directory named "toppar" in the base directory
329
+ dest_dirname = posixpath.dirname(includeString)
330
+ if dest_dirname:
331
+ # if dest_dirname is not empty
332
+ if not posixpath.exists(posixpath.join(self.baseDirectory, 'Ligand', dest_dirname)):
333
+ # if the destination directory does not exist
334
+ os.makedirs(posixpath.join(self.baseDirectory, 'Ligand', dest_dirname))
335
+ shutil.copy(includeFile, posixpath.join(self.baseDirectory, 'Ligand', dest_dirname))
336
+
337
+ #self.structureFile = shutil.copy(self.structureFile, self.baseDirectory)
338
+ self.topologyFile = shutil.copy(self.topologyFile, posixpath.join(self.baseDirectory, 'Protein'))
339
+ self.ligandOnlyStructureFile = shutil.copy(self.ligandOnlyStructureFile, posixpath.join(self.baseDirectory, 'Ligand'))
340
+ self.ligandOnlyTopologyFile = shutil.copy(self.ligandOnlyTopologyFile, posixpath.join(self.baseDirectory, 'Ligand'))
341
+
342
+ # move the system, so that the complex is at the center of the simulation box
343
+ all_center = measure_center(all_atoms.positions)
344
+ moveVector = (-all_center[0], -all_center[1], -all_center[2])
345
+ transformations.translate(moveVector)(all_atoms)
346
+ all_center = measure_center(all_atoms.positions)
347
+ moveVector = (dim[0]/2, dim[1]/2, dim[2]/2)
348
+ transformations.translate(moveVector)(all_atoms)
349
+ all_center = measure_center(all_atoms.positions)
350
+
351
+ ligandOnly_center = measure_center(all_atoms_ligandOnly.positions)
352
+ moveVector = (-ligandOnly_center[0], -ligandOnly_center[1], -ligandOnly_center[2])
353
+ transformations.translate(moveVector)(all_atoms_ligandOnly)
354
+ ligandOnly_center = measure_center(all_atoms_ligandOnly.positions)
355
+ moveVector = (ligandOnly_dim[0]/2, ligandOnly_dim[1]/2, ligandOnly_dim[2]/2)
356
+ transformations.translate(moveVector)(all_atoms_ligandOnly)
357
+ ligandOnly_center = measure_center(all_atoms_ligandOnly.positions)
358
+
359
+ _, fileName = os.path.split(self.structureFile)
360
+ all_atoms.write(self.baseDirectory + '/Protein/' + fileName)
361
+
362
+ _, ligandOnly_fileName = os.path.split(self.ligandOnlyStructureFile)
363
+ all_atoms_ligandOnly.write(self.baseDirectory + '/' + ligandOnly_fileName)
364
+
365
+ #if isclose(volume, 0.0):
366
+ #self.logger.warning(f'The volume is too small. Maybe the structure file is a PDB file without the unit cell.')
367
+ #all_atoms = self.system.select_atoms("all")
368
+ #self.logger.warning(f'The unit cell has been reset to {dim[0]:12.5f} {dim[1]:12.5f} {dim[2]:12.5f} .')
369
+ newBasename = posixpath.splitext(fileName)[0]
370
+ self.structureFile = self.baseDirectory + '/Protein/' + fileName + '.new.gro'
371
+ #self.saveStructure(self.structureFile)
372
+ all_atoms.write(self.structureFile)
373
+ # measure the cell of the ligand-only system
374
+ #dim = self.ligandOnlySystem.dimensions
375
+ #volume = dim[0] * dim[1] * dim[2]
376
+ #self.logger.info(f'The volume of the simulation box (ligand-only system) is {volume} Å^3.')
377
+ #if isclose(volume, 0.0):
378
+ #self.logger.warning(f'The volume is too small. Maybe the structure file is a PDB file without the unit cell.')
379
+ #all_atoms = self.ligandOnlySystem.select_atoms("all")
380
+ #self.ligandOnlySystem.trajectory[0].triclinic_dimensions = get_cell(all_atoms.positions)
381
+ #dim = self.ligandOnlySystem.dimensions
382
+ #self.logger.warning(f'The unit cell has been reset to {dim[0]:12.5f} {dim[1]:12.5f} {dim[2]:12.5f} .')
383
+ newBasename = posixpath.splitext(ligandOnly_fileName)[0]
384
+ self.ligandOnlyStructureFile = self.baseDirectory + '/' + ligandOnly_fileName + '.new.gro'
385
+ #self.saveStructure(self.ligandOnlyStructureFile)
386
+ all_atoms_ligandOnly.write(self.ligandOnlyStructureFile)
387
+
388
+ self.basenames = [
389
+ '000_eq',
390
+ '001_RMSD_bound',
391
+ '002_euler_theta',
392
+ '003_euler_phi',
393
+ '004_euler_psi',
394
+ '005_polar_theta',
395
+ '006_polar_phi',
396
+ '007_r',
397
+ '008_RMSD_unbound'
398
+ ]
399
+ self.stepnames = self.basenames.copy()
400
+ self.basenames = [posixpath.join(self.baseDirectory, basename) for basename in self.basenames]
401
+ self.logger.info('Initialization done.')
402
+
403
+ def saveStructure(self, outputFile, selection='all'):
404
+ """a helper method for selecting a group of atoms and save it
405
+
406
+ Args:
407
+ outputFile (str): filename of the output file
408
+ selection (str, optional): MDAnalysis atom selection string. Defaults to 'all'.
409
+
410
+ Raises:
411
+ SelectionError: if the selection corresponds to nothing
412
+ """
413
+
414
+ self.logger.info(f'Saving a new structure file at {outputFile} with selection ({selection}).')
415
+ selected_atoms = self.system.select_atoms(selection)
416
+ if len(selected_atoms) == 0:
417
+ raise SelectionError('Empty selection!')
418
+ selected_atoms.write(outputFile)
419
+
420
+ def setProteinHeavyAtomsGroup(self, selection):
421
+ """select the heavy atoms of the protein
422
+
423
+ Args:
424
+ selection (str): MDAnalysis atom selection string
425
+
426
+ Raises:
427
+ SelectionError: if the selection corresponds to nothing
428
+ """
429
+
430
+ self.logger.info(f'Setup the atoms group of the protein by selection: {selection}')
431
+ self.protein = self.system.select_atoms(selection)
432
+ if len(self.protein) == 0:
433
+ raise SelectionError('Empty selection!')
434
+
435
+ def setLigandHeavyAtomsGroup(self, selection):
436
+ """select the heavy atoms of the ligand in both the protein-ligand complex
437
+ and the ligand-only systems
438
+
439
+ Args:
440
+ selection (str): MDAnalysis atom selection string
441
+
442
+ Raises:
443
+ SelectionError: if the selection corresponds to nothing
444
+ """
445
+
446
+ self.logger.info(f'Setup the atoms group of the ligand by selection: {selection}')
447
+ self.ligand = self.system.select_atoms(selection)
448
+ if len(self.ligand) == 0:
449
+ raise SelectionError('Empty selection!')
450
+ self.ligandOnly = self.ligandOnlySystem.select_atoms(selection)
451
+ if len(self.ligandOnly) == 0:
452
+ raise SelectionError('Empty selection!')
453
+
454
+ def setSolventAtomsGroup(self, selection):
455
+ """select the solvent atoms
456
+
457
+ Args:
458
+ selection (str): MDAnalysis atom selection string
459
+
460
+ Raises:
461
+ SelectionError: if the selection corresponds nothing
462
+ """
463
+
464
+ self.logger.info(f'Setup the atoms group of the solvent molecule by selection: {selection}')
465
+ self.solvent = self.system.select_atoms(selection)
466
+ if len(self.solvent) == 0:
467
+ raise SelectionError('Empty selection!')
468
+
469
+ def setTemperature(self, newTemperature):
470
+ """set the temperature
471
+
472
+ Args:
473
+ newTemperature (float): new value of the temperature
474
+ """
475
+ self.temperature = newTemperature
476
+
477
+ def generateGromacsIndex(self, outputFile):
478
+ """generate a GROMACS index file for atom selection in Colvars
479
+ """
480
+
481
+ self.system.select_atoms('all').write(outputFile, name='BFEE_all')
482
+ if hasattr(self, 'ligand'):
483
+ self.ligand.write(outputFile, name='BFEE_Ligand', mode='a')
484
+ if hasattr(self, 'protein'):
485
+ self.protein.write(outputFile, name='BFEE_Protein', mode='a')
486
+ if hasattr(self, 'solvent'):
487
+ self.solvent.write(outputFile, name='BFEE_Solvent', mode='a')
488
+
489
+ def generate000(self):
490
+ """generate files for running an equilibrium simulation
491
+ """
492
+
493
+ self.handler.setFormatter(logging.Formatter('%(asctime)s [BFEEGromacs][000][%(levelname)s]:%(message)s'))
494
+ generate_basename = self.basenames[0]
495
+ self.logger.info('=' * 80)
496
+ self.logger.info(f'Generating simulation files for {generate_basename}...')
497
+ if not posixpath.exists(generate_basename):
498
+ self.logger.info(f'Making directory {os.path.abspath(generate_basename)}...')
499
+ os.makedirs(generate_basename)
500
+ # generate the MDP file
501
+ colvars_inputfile_basename = posixpath.join(generate_basename, '000_colvars')
502
+ generateMDP(pkg_resources.read_text(templates_gromacs, '000.mdp.template'),
503
+ posixpath.join(generate_basename, '000_eq'),
504
+ logger=self.logger,
505
+ timeStep=0.002,
506
+ numSteps=4000000,
507
+ temperature=self.temperature,
508
+ pressure=1.01325,
509
+ colvarsFile='000_colvars.dat')
510
+ # check if the ligand and protein is selected
511
+ if not hasattr(self, 'ligand'):
512
+ raise RuntimeError('The atoms of the ligand have not been selected.')
513
+ if not hasattr(self, 'protein'):
514
+ raise RuntimeError('The atoms of the protein have not been selected.')
515
+ # measure the COM of the protein
516
+ protein_center = measure_center(self.protein.positions)
517
+ # convert angstrom to nanometer and format the string
518
+ protein_center = convert(protein_center, "angstrom", "nm")
519
+ protein_center_str = f'({protein_center[0]}, {protein_center[1]}, {protein_center[2]})'
520
+ self.logger.info('COM of the protein: ' + protein_center_str + '.')
521
+ # generate the index file
522
+ self.generateGromacsIndex(posixpath.join(generate_basename, 'colvars.ndx'))
523
+ # generate the colvars configuration
524
+ generateColvars(pkg_resources.read_text(templates_gromacs, '000.colvars.template'),
525
+ colvars_inputfile_basename,
526
+ protein_selection='BFEE_Protein',
527
+ protein_center=protein_center_str,
528
+ logger=self.logger)
529
+ # generate the reference file
530
+ self.system.select_atoms('all').write(posixpath.join(generate_basename, 'reference.xyz'))
531
+ # generate the shell script for making the tpr file
532
+ generateShellScript(pkg_resources.read_text(templates_gromacs, '000.generate_tpr_sh.template'),
533
+ posixpath.join(generate_basename, '000_generate_tpr'),
534
+ logger=self.logger,
535
+ MDP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
536
+ '000_eq.mdp')),
537
+ os.path.abspath(generate_basename)).replace('\\', '/'),
538
+ GRO_FILE_TEMPLATE=os.path.relpath(os.path.abspath(self.structureFile),
539
+ os.path.abspath(generate_basename)).replace('\\', '/'),
540
+ TOP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(self.topologyFile),
541
+ os.path.abspath(generate_basename)).replace('\\', '/'),
542
+ COLVARS_INPUT_TEMPLATE=os.path.relpath(os.path.abspath(colvars_inputfile_basename + '.dat'),
543
+ os.path.abspath(generate_basename)).replace('\\', '/'))
544
+ if not posixpath.exists(posixpath.join(generate_basename, 'output')):
545
+ os.makedirs(posixpath.join(generate_basename, 'output'))
546
+ self.logger.info(f"Generation of {generate_basename} done.")
547
+ self.logger.info('=' * 80)
548
+
549
+
550
+ def generate001(self):
551
+ """generate files for determining the PMF along the RMSD of the ligand
552
+ with respect to its bound state
553
+ """
554
+
555
+ self.handler.setFormatter(logging.Formatter('%(asctime)s [BFEEGromacs][001][%(levelname)s]:%(message)s'))
556
+ generate_basename = self.basenames[1]
557
+ self.logger.info('=' * 80)
558
+ self.logger.info(f'Generating simulation files for {generate_basename}...')
559
+ if not posixpath.exists(generate_basename):
560
+ self.logger.info(f'Making directory {os.path.abspath(generate_basename)}...')
561
+ os.makedirs(generate_basename)
562
+ # generate the MDP file
563
+ colvars_inputfile_basename = posixpath.join(generate_basename, '001_colvars')
564
+ generateMDP(pkg_resources.read_text(templates_gromacs, '001.mdp.template'),
565
+ posixpath.join(generate_basename, '001_PMF'),
566
+ logger=self.logger,
567
+ timeStep=0.002,
568
+ numSteps=4000000,
569
+ temperature=self.temperature,
570
+ pressure=1.01325,
571
+ colvarsFile='001_colvars.dat')
572
+ # check if the ligand and protein is selected
573
+ if not hasattr(self, 'ligand'):
574
+ raise RuntimeError('The atoms of the ligand have not been selected.')
575
+ if not hasattr(self, 'protein'):
576
+ raise RuntimeError('The atoms of the protein have not been selected.')
577
+ # measure the COM of the protein
578
+ protein_center = measure_center(self.protein.positions)
579
+ # convert angstrom to nanometer and format the string
580
+ protein_center = convert(protein_center, "angstrom", "nm")
581
+ protein_center_str = f'({protein_center[0]}, {protein_center[1]}, {protein_center[2]})'
582
+ self.logger.info('COM of the protein: ' + protein_center_str + '.')
583
+ # generate the index file
584
+ self.generateGromacsIndex(posixpath.join(generate_basename, 'colvars.ndx'))
585
+ # generate the colvars configuration
586
+ generateColvars(pkg_resources.read_text(templates_gromacs, '001.colvars.template'),
587
+ colvars_inputfile_basename,
588
+ rmsd_bin_width=0.005,
589
+ rmsd_lower_boundary=0.0,
590
+ rmsd_upper_boundary=0.5,
591
+ rmsd_wall_constant=0.8368,
592
+ ligand_selection='BFEE_Ligand',
593
+ protein_selection='BFEE_Protein',
594
+ protein_center=protein_center_str,
595
+ logger=self.logger)
596
+ # generate the reference file
597
+ self.system.select_atoms('all').write(posixpath.join(generate_basename, 'reference.xyz'))
598
+ # generate the shell script for making the tpr file
599
+ generateShellScript(pkg_resources.read_text(templates_gromacs, '001.generate_tpr_sh.template'),
600
+ posixpath.join(generate_basename, '001_generate_tpr'),
601
+ logger=self.logger,
602
+ MDP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
603
+ '001_PMF.mdp')),
604
+ os.path.abspath(generate_basename)).replace('\\', '/'),
605
+ GRO_FILE_TEMPLATE=os.path.relpath(os.path.abspath(f'{self.baseDirectory}/000_eq/output/000_eq.out.gro'),
606
+ os.path.abspath(generate_basename)).replace('\\', '/'),
607
+ TOP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(self.topologyFile),
608
+ os.path.abspath(generate_basename)).replace('\\', '/'),
609
+ COLVARS_INPUT_TEMPLATE=os.path.relpath(os.path.abspath(colvars_inputfile_basename + '.dat'),
610
+ os.path.abspath(generate_basename)).replace('\\', '/'))
611
+ if not posixpath.exists(posixpath.join(generate_basename, 'output')):
612
+ os.makedirs(posixpath.join(generate_basename, 'output'))
613
+ self.logger.info(f"Generation of {generate_basename} done.")
614
+ self.logger.info('=' * 80)
615
+
616
+ def generate002(self):
617
+ """generate files for determining the PMF along the pitch (theta) angle of
618
+ the ligand
619
+ """
620
+
621
+ self.handler.setFormatter(logging.Formatter('%(asctime)s [BFEEGromacs][002][%(levelname)s]:%(message)s'))
622
+ generate_basename = self.basenames[2]
623
+ self.logger.info('=' * 80)
624
+ self.logger.info(f'Generating simulation files for {generate_basename}...')
625
+ if not posixpath.exists(generate_basename):
626
+ self.logger.info(f'Making directory {os.path.abspath(generate_basename)}...')
627
+ os.makedirs(generate_basename)
628
+ # generate the MDP file
629
+ colvars_inputfile_basename = posixpath.join(generate_basename, '002_colvars')
630
+ generateMDP(pkg_resources.read_text(templates_gromacs, '002.mdp.template'),
631
+ posixpath.join(generate_basename, '002_PMF'),
632
+ timeStep=0.002,
633
+ numSteps=4000000,
634
+ temperature=self.temperature,
635
+ pressure=1.01325,
636
+ logger=self.logger,
637
+ colvarsFile='002_colvars.dat')
638
+ # check if the ligand and protein is selected
639
+ if not hasattr(self, 'ligand'):
640
+ raise RuntimeError('The atoms of the ligand have not been selected.')
641
+ if not hasattr(self, 'protein'):
642
+ raise RuntimeError('The atoms of the protein have not been selected.')
643
+ # measure the COM of the protein
644
+ protein_center = measure_center(self.protein.positions)
645
+ # convert angstrom to nanometer and format the string
646
+ protein_center = convert(protein_center, "angstrom", "nm")
647
+ protein_center_str = f'({protein_center[0]}, {protein_center[1]}, {protein_center[2]})'
648
+ self.logger.info('COM of the protein: ' + protein_center_str + '.')
649
+ # generate the index file
650
+ self.generateGromacsIndex(posixpath.join(generate_basename, 'colvars.ndx'))
651
+ # generate the colvars configuration
652
+ generateColvars(pkg_resources.read_text(templates_gromacs, '002.colvars.template'),
653
+ colvars_inputfile_basename,
654
+ logger=self.logger,
655
+ eulerTheta_width=1,
656
+ eulerTheta_lower_boundary=-10.0,
657
+ eulerTheta_upper_boundary=10.0,
658
+ eulerTheta_wall_constant=0.8368,
659
+ ligand_selection='BFEE_Ligand',
660
+ protein_selection='BFEE_Protein',
661
+ protein_center=protein_center_str)
662
+ # generate the reference file
663
+ self.system.select_atoms('all').write(posixpath.join(generate_basename, 'reference.xyz'))
664
+ # generate the shell script for making the tpr file
665
+ generateShellScript(pkg_resources.read_text(templates_gromacs, '002.generate_tpr_sh.template'),
666
+ posixpath.join(generate_basename, '002_generate_tpr'),
667
+ logger=self.logger,
668
+ MDP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
669
+ '002_PMF.mdp')),
670
+ os.path.abspath(generate_basename)).replace('\\', '/'),
671
+ GRO_FILE_TEMPLATE=os.path.relpath(os.path.abspath(f'{self.baseDirectory}/000_eq/output/000_eq.out.gro'),
672
+ os.path.abspath(generate_basename)).replace('\\', '/'),
673
+ TOP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(self.topologyFile),
674
+ os.path.abspath(generate_basename)).replace('\\', '/'),
675
+ COLVARS_INPUT_TEMPLATE=os.path.relpath(os.path.abspath(colvars_inputfile_basename + '.dat'),
676
+ os.path.abspath(generate_basename)).replace('\\', '/'))
677
+ # also copy the awk script to modify the colvars configuration according to the PMF minima in previous stages
678
+ with pkg_resources.path(templates_gromacs, 'find_min_max.awk') as p:
679
+ shutil.copyfile(p, posixpath.join(generate_basename, 'find_min_max.awk'))
680
+ if not posixpath.exists(posixpath.join(generate_basename, 'output')):
681
+ os.makedirs(posixpath.join(generate_basename, 'output'))
682
+ self.logger.info(f"Generation of {generate_basename} done.")
683
+ self.logger.info('=' * 80)
684
+
685
+ def generate003(self):
686
+ """generate files for determining the PMF along the roll (phi) angle of
687
+ the ligand
688
+ """
689
+
690
+ self.handler.setFormatter(logging.Formatter('%(asctime)s [BFEEGromacs][003][%(levelname)s]:%(message)s'))
691
+ generate_basename = self.basenames[3]
692
+ self.logger.info('=' * 80)
693
+ self.logger.info(f'Generating simulation files for {generate_basename}...')
694
+ if not posixpath.exists(generate_basename):
695
+ self.logger.info(f'Making directory {os.path.abspath(generate_basename)}...')
696
+ os.makedirs(generate_basename)
697
+ # generate the MDP file
698
+ colvars_inputfile_basename = posixpath.join(generate_basename, '003_colvars')
699
+ generateMDP(pkg_resources.read_text(templates_gromacs, '003.mdp.template'),
700
+ posixpath.join(generate_basename, '003_PMF'),
701
+ timeStep=0.002,
702
+ numSteps=4000000,
703
+ temperature=self.temperature,
704
+ pressure=1.01325,
705
+ colvarsFile='003_colvars.dat',
706
+ logger=self.logger)
707
+ # check if the ligand and protein is selected
708
+ if not hasattr(self, 'ligand'):
709
+ raise RuntimeError('The atoms of the ligand have not been selected.')
710
+ if not hasattr(self, 'protein'):
711
+ raise RuntimeError('The atoms of the protein have not been selected.')
712
+ # measure the COM of the protein
713
+ protein_center = measure_center(self.protein.positions)
714
+ # convert angstrom to nanometer and format the string
715
+ protein_center = convert(protein_center, "angstrom", "nm")
716
+ protein_center_str = f'({protein_center[0]}, {protein_center[1]}, {protein_center[2]})'
717
+ self.logger.info('COM of the protein: ' + protein_center_str + '.')
718
+ # generate the index file
719
+ self.generateGromacsIndex(posixpath.join(generate_basename, 'colvars.ndx'))
720
+ # generate the colvars configuration
721
+ generateColvars(pkg_resources.read_text(templates_gromacs, '003.colvars.template'),
722
+ colvars_inputfile_basename,
723
+ logger=self.logger,
724
+ eulerPhi_width=1,
725
+ eulerPhi_lower_boundary=-10.0,
726
+ eulerPhi_upper_boundary=10.0,
727
+ eulerPhi_wall_constant=0.8368,
728
+ ligand_selection='BFEE_Ligand',
729
+ protein_selection='BFEE_Protein',
730
+ protein_center=protein_center_str)
731
+ # generate the reference file
732
+ self.system.select_atoms('all').write(posixpath.join(generate_basename, 'reference.xyz'))
733
+ # generate the shell script for making the tpr file
734
+ generateShellScript(pkg_resources.read_text(templates_gromacs, '003.generate_tpr_sh.template'),
735
+ posixpath.join(generate_basename, '003_generate_tpr'),
736
+ logger=self.logger,
737
+ BASENAME_002=self.stepnames[2],
738
+ MDP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
739
+ '003_PMF.mdp')),
740
+ os.path.abspath(generate_basename)).replace('\\', '/'),
741
+ GRO_FILE_TEMPLATE=os.path.relpath(os.path.abspath(f'{self.baseDirectory}/000_eq/output/000_eq.out.gro'),
742
+ os.path.abspath(generate_basename)).replace('\\', '/'),
743
+ TOP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(self.topologyFile),
744
+ os.path.abspath(generate_basename)).replace('\\', '/'),
745
+ COLVARS_INPUT_TEMPLATE=os.path.relpath(os.path.abspath(colvars_inputfile_basename + '.dat'),
746
+ os.path.abspath(generate_basename)).replace('\\', '/'))
747
+ # also copy the awk script to modify the colvars configuration according to the PMF minima in previous stages
748
+ with pkg_resources.path(templates_gromacs, 'find_min_max.awk') as p:
749
+ shutil.copyfile(p, posixpath.join(generate_basename, 'find_min_max.awk'))
750
+ if not posixpath.exists(posixpath.join(generate_basename, 'output')):
751
+ os.makedirs(posixpath.join(generate_basename, 'output'))
752
+ self.logger.info(f"Generation of {generate_basename} done.")
753
+ self.logger.info('=' * 80)
754
+
755
+ def generate004(self):
756
+ """generate files for determining the PMF along the yaw (psi) angle of
757
+ the ligand
758
+ """
759
+
760
+ self.handler.setFormatter(logging.Formatter('%(asctime)s [BFEEGromacs][004][%(levelname)s]:%(message)s'))
761
+ generate_basename = self.basenames[4]
762
+ self.logger.info('=' * 80)
763
+ self.logger.info(f'Generating simulation files for {generate_basename}...')
764
+ if not posixpath.exists(generate_basename):
765
+ self.logger.info(f'Making directory {os.path.abspath(generate_basename)}...')
766
+ os.makedirs(generate_basename)
767
+ # generate the MDP file
768
+ colvars_inputfile_basename = posixpath.join(generate_basename, '004_colvars')
769
+ generateMDP(pkg_resources.read_text(templates_gromacs, '004.mdp.template'),
770
+ posixpath.join(generate_basename, '004_PMF'),
771
+ timeStep=0.002,
772
+ numSteps=4000000,
773
+ temperature=self.temperature,
774
+ pressure=1.01325,
775
+ colvarsFile='004_colvars.dat',
776
+ logger=self.logger)
777
+ # check if the ligand and protein is selected
778
+ if not hasattr(self, 'ligand'):
779
+ raise RuntimeError('The atoms of the ligand have not been selected.')
780
+ if not hasattr(self, 'protein'):
781
+ raise RuntimeError('The atoms of the protein have not been selected.')
782
+ # measure the COM of the protein
783
+ protein_center = measure_center(self.protein.positions)
784
+ # convert angstrom to nanometer and format the string
785
+ protein_center = convert(protein_center, "angstrom", "nm")
786
+ protein_center_str = f'({protein_center[0]}, {protein_center[1]}, {protein_center[2]})'
787
+ self.logger.info('COM of the protein: ' + protein_center_str + '.')
788
+ # generate the index file
789
+ self.generateGromacsIndex(posixpath.join(generate_basename, 'colvars.ndx'))
790
+ # generate the colvars configuration
791
+ generateColvars(pkg_resources.read_text(templates_gromacs, '004.colvars.template'),
792
+ colvars_inputfile_basename,
793
+ logger=self.logger,
794
+ eulerPsi_width=1,
795
+ eulerPsi_lower_boundary=-10.0,
796
+ eulerPsi_upper_boundary=10.0,
797
+ eulerPsi_wall_constant=0.8368,
798
+ ligand_selection='BFEE_Ligand',
799
+ protein_selection='BFEE_Protein',
800
+ protein_center=protein_center_str)
801
+ # generate the reference file
802
+ self.system.select_atoms('all').write(posixpath.join(generate_basename, 'reference.xyz'))
803
+ # generate the shell script for making the tpr file
804
+ generateShellScript(pkg_resources.read_text(templates_gromacs, '004.generate_tpr_sh.template'),
805
+ posixpath.join(generate_basename, '004_generate_tpr'),
806
+ logger=self.logger,
807
+ BASENAME_002=self.stepnames[2],
808
+ BASENAME_003=self.stepnames[3],
809
+ MDP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
810
+ '004_PMF.mdp')),
811
+ os.path.abspath(generate_basename)).replace('\\', '/'),
812
+ GRO_FILE_TEMPLATE=os.path.relpath(os.path.abspath(f'{self.baseDirectory}/000_eq/output/000_eq.out.gro'),
813
+ os.path.abspath(generate_basename)).replace('\\', '/'),
814
+ TOP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(self.topologyFile),
815
+ os.path.abspath(generate_basename)).replace('\\', '/'),
816
+ COLVARS_INPUT_TEMPLATE=os.path.relpath(os.path.abspath(colvars_inputfile_basename + '.dat'),
817
+ os.path.abspath(generate_basename)).replace('\\', '/'))
818
+ # also copy the awk script to modify the colvars configuration according to the PMF minima in previous stages
819
+ with pkg_resources.path(templates_gromacs, 'find_min_max.awk') as p:
820
+ shutil.copyfile(p, posixpath.join(generate_basename, 'find_min_max.awk'))
821
+ if not posixpath.exists(posixpath.join(generate_basename, 'output')):
822
+ os.makedirs(posixpath.join(generate_basename, 'output'))
823
+ self.logger.info(f"Generation of {generate_basename} done.")
824
+ self.logger.info('=' * 80)
825
+
826
+ def generate005(self):
827
+ """generate files for determining the PMF along the polar theta angle of
828
+ the ligand relative to the protein
829
+ """
830
+
831
+ self.handler.setFormatter(logging.Formatter('%(asctime)s [BFEEGromacs][005][%(levelname)s]:%(message)s'))
832
+ generate_basename = self.basenames[5]
833
+ self.logger.info('=' * 80)
834
+ self.logger.info(f'Generating simulation files for {generate_basename}...')
835
+ if not posixpath.exists(generate_basename):
836
+ self.logger.info(f'Making directory {os.path.abspath(generate_basename)}...')
837
+ os.makedirs(generate_basename)
838
+ # generate the MDP file
839
+ colvars_inputfile_basename = posixpath.join(generate_basename, '005_colvars')
840
+ generateMDP(pkg_resources.read_text(templates_gromacs, '005.mdp.template'),
841
+ posixpath.join(generate_basename, '005_PMF'),
842
+ timeStep=0.002,
843
+ numSteps=4000000,
844
+ temperature=self.temperature,
845
+ pressure=1.01325,
846
+ colvarsFile='005_colvars.dat',
847
+ logger=self.logger)
848
+ # check if the ligand and protein is selected
849
+ if not hasattr(self, 'ligand'):
850
+ raise RuntimeError('The atoms of the ligand have not been selected.')
851
+ if not hasattr(self, 'protein'):
852
+ raise RuntimeError('The atoms of the protein have not been selected.')
853
+ # measure the COM of the protein
854
+ protein_center = measure_center(self.protein.positions)
855
+ # convert angstrom to nanometer and format the string
856
+ protein_center = convert(protein_center, "angstrom", "nm")
857
+ protein_center_str = f'({protein_center[0]}, {protein_center[1]}, {protein_center[2]})'
858
+ self.logger.info('COM of the protein: ' + protein_center_str + '.')
859
+ # generate the index file
860
+ self.generateGromacsIndex(posixpath.join(generate_basename, 'colvars.ndx'))
861
+ # measure the current polar theta angles
862
+ ligand_center = measure_center(self.ligand.positions)
863
+ ligand_center = convert(ligand_center, "angstrom", "nm")
864
+ # ligand_center_str = f'({ligand_center[0]}, {ligand_center[1]}, {ligand_center[2]})'
865
+ polar_theta, polar_phi = mearsurePolarAngles(protein_center, ligand_center)
866
+ polar_theta_center = np.around(polar_theta, 1)
867
+ self.logger.info(f'Measured polar angles: theta = {polar_theta:12.5f} ; phi = {polar_phi:12.5f}')
868
+ polar_theta_width = 1
869
+ polar_theta_lower = polar_theta_center - polar_theta_width * np.ceil(10 / polar_theta_width)
870
+ polar_theta_upper = polar_theta_center + polar_theta_width * np.ceil(10 / polar_theta_width)
871
+ # generate the colvars configuration
872
+ generateColvars(pkg_resources.read_text(templates_gromacs, '005.colvars.template'),
873
+ colvars_inputfile_basename,
874
+ logger=self.logger,
875
+ polarTheta_width=polar_theta_width,
876
+ polarTheta_lower_boundary=np.around(polar_theta_lower, 2),
877
+ polarTheta_upper_boundary=np.around(polar_theta_upper, 2),
878
+ polarTheta_wall_constant=0.8368,
879
+ ligand_selection='BFEE_Ligand',
880
+ protein_selection='BFEE_Protein',
881
+ protein_center=protein_center_str)
882
+ # generate the reference file
883
+ self.system.select_atoms('all').write(posixpath.join(generate_basename, 'reference.xyz'))
884
+ # generate the shell script for making the tpr file
885
+ generateShellScript(pkg_resources.read_text(templates_gromacs, '005.generate_tpr_sh.template'),
886
+ posixpath.join(generate_basename, '005_generate_tpr'),
887
+ logger=self.logger,
888
+ BASENAME_002=self.stepnames[2],
889
+ BASENAME_003=self.stepnames[3],
890
+ BASENAME_004=self.stepnames[4],
891
+ MDP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
892
+ '005_PMF.mdp')),
893
+ os.path.abspath(generate_basename)).replace('\\', '/'),
894
+ GRO_FILE_TEMPLATE=os.path.relpath(os.path.abspath(f'{self.baseDirectory}/000_eq/output/000_eq.out.gro'),
895
+ os.path.abspath(generate_basename)).replace('\\', '/'),
896
+ TOP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(self.topologyFile),
897
+ os.path.abspath(generate_basename)).replace('\\', '/'),
898
+ COLVARS_INPUT_TEMPLATE=os.path.relpath(os.path.abspath(colvars_inputfile_basename + '.dat'),
899
+ os.path.abspath(generate_basename)).replace('\\', '/'))
900
+ # also copy the awk script to modify the colvars configuration according to the PMF minima in previous stages
901
+ with pkg_resources.path(templates_gromacs, 'find_min_max.awk') as p:
902
+ shutil.copyfile(p, posixpath.join(generate_basename, 'find_min_max.awk'))
903
+ if not posixpath.exists(posixpath.join(generate_basename, 'output')):
904
+ os.makedirs(posixpath.join(generate_basename, 'output'))
905
+ self.logger.info(f"Generation of {generate_basename} done.")
906
+ self.logger.info('=' * 80)
907
+
908
+ def generate006(self):
909
+ """generate files for determining the PMF along the polar phi angle of
910
+ the ligand relative to the protein
911
+ """
912
+
913
+ self.handler.setFormatter(logging.Formatter('%(asctime)s [BFEEGromacs][006][%(levelname)s]:%(message)s'))
914
+ generate_basename = self.basenames[6]
915
+ self.logger.info('=' * 80)
916
+ self.logger.info(f'Generating simulation files for {generate_basename}...')
917
+ if not posixpath.exists(generate_basename):
918
+ self.logger.info(f'Making directory {os.path.abspath(generate_basename)}...')
919
+ os.makedirs(generate_basename)
920
+ # generate the MDP file
921
+ colvars_inputfile_basename = posixpath.join(generate_basename, '006_colvars')
922
+ generateMDP(pkg_resources.read_text(templates_gromacs, '006.mdp.template'),
923
+ posixpath.join(generate_basename, '006_PMF'),
924
+ timeStep=0.002,
925
+ numSteps=4000000,
926
+ temperature=self.temperature,
927
+ pressure=1.01325,
928
+ colvarsFile='006_colvars.dat',
929
+ logger=self.logger)
930
+ # check if the ligand and protein is selected
931
+ if not hasattr(self, 'ligand'):
932
+ raise RuntimeError('The atoms of the ligand have not been selected.')
933
+ if not hasattr(self, 'protein'):
934
+ raise RuntimeError('The atoms of the protein have not been selected.')
935
+ # measure the COM of the protein
936
+ protein_center = measure_center(self.protein.positions)
937
+ # convert angstrom to nanometer and format the string
938
+ protein_center = convert(protein_center, "angstrom", "nm")
939
+ protein_center_str = f'({protein_center[0]}, {protein_center[1]}, {protein_center[2]})'
940
+ self.logger.info('COM of the protein: ' + protein_center_str + '.')
941
+ # generate the index file
942
+ self.generateGromacsIndex(posixpath.join(generate_basename, 'colvars.ndx'))
943
+ # measure the current polar theta angles
944
+ ligand_center = measure_center(self.ligand.positions)
945
+ ligand_center = convert(ligand_center, "angstrom", "nm")
946
+ # ligand_center_str = f'({ligand_center[0]}, {ligand_center[1]}, {ligand_center[2]})'
947
+ polar_theta, polar_phi = mearsurePolarAngles(protein_center, ligand_center)
948
+ polar_phi_center = np.around(polar_phi, 1)
949
+ self.logger.info(f'Measured polar angles: theta = {polar_theta:12.5f} ; phi = {polar_phi:12.5f}')
950
+ polar_phi_width = 1
951
+ polar_phi_lower = polar_phi_center - polar_phi_width * np.ceil(10 / polar_phi_width)
952
+ polar_phi_upper = polar_phi_center + polar_phi_width * np.ceil(10 / polar_phi_width)
953
+ # generate the colvars configuration
954
+ generateColvars(pkg_resources.read_text(templates_gromacs, '006.colvars.template'),
955
+ colvars_inputfile_basename,
956
+ logger=self.logger,
957
+ polarPhi_width=polar_phi_width,
958
+ polarPhi_lower_boundary=np.around(polar_phi_lower, 2),
959
+ polarPhi_upper_boundary=np.around(polar_phi_upper, 2),
960
+ polarPhi_wall_constant=0.8368,
961
+ ligand_selection='BFEE_Ligand',
962
+ protein_selection='BFEE_Protein',
963
+ protein_center=protein_center_str)
964
+ # generate the reference file
965
+ self.system.select_atoms('all').write(posixpath.join(generate_basename, 'reference.xyz'))
966
+ # generate the shell script for making the tpr file
967
+ generateShellScript(pkg_resources.read_text(templates_gromacs, '006.generate_tpr_sh.template'),
968
+ posixpath.join(generate_basename, '006_generate_tpr'),
969
+ logger=self.logger,
970
+ BASENAME_002=self.stepnames[2],
971
+ BASENAME_003=self.stepnames[3],
972
+ BASENAME_004=self.stepnames[4],
973
+ BASENAME_005=self.stepnames[5],
974
+ MDP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
975
+ '006_PMF.mdp')),
976
+ os.path.abspath(generate_basename)).replace('\\', '/'),
977
+ GRO_FILE_TEMPLATE=os.path.relpath(os.path.abspath(f'{self.baseDirectory}/000_eq/output/000_eq.out.gro'),
978
+ os.path.abspath(generate_basename)).replace('\\', '/'),
979
+ TOP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(self.topologyFile),
980
+ os.path.abspath(generate_basename)).replace('\\', '/'),
981
+ COLVARS_INPUT_TEMPLATE=os.path.relpath(os.path.abspath(colvars_inputfile_basename + '.dat'),
982
+ os.path.abspath(generate_basename)).replace('\\', '/'))
983
+ # also copy the awk script to modify the colvars configuration according to the PMF minima in previous stages
984
+ with pkg_resources.path(templates_gromacs, 'find_min_max.awk') as p:
985
+ shutil.copyfile(p, posixpath.join(generate_basename, 'find_min_max.awk'))
986
+ if not posixpath.exists(posixpath.join(generate_basename, 'output')):
987
+ os.makedirs(posixpath.join(generate_basename, 'output'))
988
+ self.logger.info(f"Generation of {generate_basename} done.")
989
+ self.logger.info('=' * 80)
990
+
991
+ def generate007(self):
992
+ """generate files for determining the PMF along the distance between the
993
+ the ligand and the protein
994
+ """
995
+
996
+ self.handler.setFormatter(logging.Formatter('%(asctime)s [BFEEGromacs][007][%(levelname)s]:%(message)s'))
997
+ generate_basename = self.basenames[7]
998
+ self.logger.info('=' * 80)
999
+ self.logger.info(f'Generating simulation files for {generate_basename}...')
1000
+ if not posixpath.exists(generate_basename):
1001
+ self.logger.info(f'Making directory {os.path.abspath(generate_basename)}...')
1002
+ os.makedirs(generate_basename)
1003
+ # check if the topologies have other itp files included
1004
+ topologyIncludeFiles, topologyIncludeStrings = scanGromacsTopologyInclude(self.topologyFile)
1005
+ for includeFile, includeString in zip(topologyIncludeFiles, topologyIncludeStrings):
1006
+ # handle something like "#include "toppar/xxx.itp""
1007
+ # in this case we need to create the directory named "toppar" in the base directory
1008
+ dest_dirname = posixpath.dirname(includeString)
1009
+ if dest_dirname:
1010
+ # if dest_dirname is not empty
1011
+ if not posixpath.exists(posixpath.join(self.baseDirectory, generate_basename, dest_dirname)):
1012
+ # if the destination directory does not exist
1013
+ os.makedirs(posixpath.join(self.baseDirectory, generate_basename, dest_dirname))
1014
+ shutil.copy(includeFile, posixpath.join(self.baseDirectory, generate_basename, dest_dirname))
1015
+ # Colvars filenames
1016
+ colvars_inputfile_basename_eq = posixpath.join(generate_basename, '007_eq_colvars')
1017
+ colvars_inputfile_basename = posixpath.join(generate_basename, '007_colvars')
1018
+ # generate the MDP file for minimization (Colvars is actually disabled in the MDP template)
1019
+ generateMDP(pkg_resources.read_text(templates_gromacs, '007_min.mdp.template'),
1020
+ posixpath.join(generate_basename, '007_Minimize'),
1021
+ timeStep=0.002,
1022
+ numSteps=5000,
1023
+ temperature=self.temperature,
1024
+ pressure=1.01325,
1025
+ colvarsFile='007_eq_colvars.dat',
1026
+ logger=self.logger)
1027
+ # equilibration
1028
+ generateMDP(pkg_resources.read_text(templates_gromacs, '007.mdp.template'),
1029
+ posixpath.join(generate_basename, '007_Equilibration'),
1030
+ timeStep=0.002,
1031
+ numSteps=5000000,
1032
+ temperature=self.temperature,
1033
+ pressure=1.01325,
1034
+ colvarsFile='007_eq_colvars.dat',
1035
+ logger=self.logger)
1036
+ # free-energy calculation
1037
+ generateMDP(pkg_resources.read_text(templates_gromacs, '007.mdp.template'),
1038
+ posixpath.join(generate_basename, '007_PMF'),
1039
+ timeStep=0.002,
1040
+ numSteps=80000000,
1041
+ temperature=self.temperature,
1042
+ pressure=1.01325,
1043
+ colvarsFile='007_colvars.dat',
1044
+ logger=self.logger)
1045
+ # check if the ligand, protein and solvent is selected
1046
+ if not hasattr(self, 'ligand'):
1047
+ raise RuntimeError('The atoms of the ligand have not been selected.')
1048
+ if not hasattr(self, 'protein'):
1049
+ raise RuntimeError('The atoms of the protein have not been selected.')
1050
+ if not hasattr(self, 'solvent'):
1051
+ raise RuntimeError('The atoms of the solvent have not been selected.')
1052
+ # measure the COM of the protein
1053
+ protein_center = measure_center(self.protein.positions)
1054
+ # convert angstrom to nanometer and format the string
1055
+ protein_center = convert(protein_center, "angstrom", "nm")
1056
+ protein_center_str = f'({protein_center[0]}, {protein_center[1]}, {protein_center[2]})'
1057
+ self.logger.info('COM of the protein: ' + protein_center_str + '.')
1058
+ # generate the index file
1059
+ self.generateGromacsIndex(posixpath.join(generate_basename, 'colvars.ndx'))
1060
+ # measure the current COM distance from the ligand to protein
1061
+ ligand_center = measure_center(self.ligand.positions)
1062
+ ligand_center = convert(ligand_center, "angstrom", "nm")
1063
+ ligand_center_str = f'({ligand_center[0]}, {ligand_center[1]}, {ligand_center[2]})'
1064
+ self.logger.info('COM of the ligand: ' + ligand_center_str + '.')
1065
+ r_center = np.sqrt(np.dot(ligand_center - protein_center, ligand_center - protein_center))
1066
+ # round r_center
1067
+ r_center = np.around(r_center, 2)
1068
+ self.logger.info('Distance of protein and ligand: ' + str(r_center) + ' nm.')
1069
+ r_width = 0.01
1070
+ # r_lower_boundary = r_center - r_lower_shift
1071
+ # r_lower_shift is default to 0.2 nm
1072
+ r_lower_shift = 0.2
1073
+ r_lower_boundary = r_center - r_lower_shift
1074
+ if r_lower_boundary < 0:
1075
+ r_lower_boundary = 0.0
1076
+ # r_upper_boundary = r_center + r_upper_shift
1077
+ # r_upper_shift is default to 2.1 nm
1078
+ # also we will need r_upper_shift to enlarge the solvent box
1079
+ r_upper_shift = 2.1
1080
+ r_upper_boundary = r_center + r_upper_shift
1081
+ # generate the colvars configuration
1082
+ # colvars file for equilibration
1083
+ generateColvars(pkg_resources.read_text(templates_gromacs, '007_eq.colvars.template'),
1084
+ colvars_inputfile_basename_eq,
1085
+ logger=self.logger,
1086
+ ligand_selection='BFEE_Ligand',
1087
+ protein_selection='BFEE_Protein',
1088
+ protein_center=protein_center_str)
1089
+ # colvars file for free-energy calculation
1090
+ generateColvars(pkg_resources.read_text(templates_gromacs, '007.colvars.template'),
1091
+ colvars_inputfile_basename,
1092
+ logger=self.logger,
1093
+ r_width=r_width,
1094
+ r_lower_boundary=r_lower_boundary,
1095
+ r_upper_boundary=r_upper_boundary,
1096
+ r_wall_constant=0.5*4.184,
1097
+ ligand_selection='BFEE_Ligand',
1098
+ protein_selection='BFEE_Protein',
1099
+ protein_center=protein_center_str)
1100
+ # generate the reference file
1101
+ self.system.select_atoms('all').write(posixpath.join(generate_basename, 'reference.xyz'))
1102
+ # write the solvent molecules
1103
+ self.solvent.write(posixpath.join(generate_basename, 'solvent.gro'))
1104
+ # generate the shell script for making the tpr file
1105
+ # further enlarge the water box by 10% since the size of box may be compressed under NPT
1106
+ new_box_x = np.around(convert(self.system.dimensions[0], 'angstrom', 'nm'), 2) + r_upper_shift * 1.1
1107
+ new_box_y = np.around(convert(self.system.dimensions[1], 'angstrom', 'nm'), 2) + r_upper_shift * 1.1
1108
+ new_box_z = np.around(convert(self.system.dimensions[2], 'angstrom', 'nm'), 2) + r_upper_shift * 1.1
1109
+ # generate shell script for equlibration
1110
+ generateShellScript(pkg_resources.read_text(templates_gromacs, '007_eq.generate_tpr_sh.template'),
1111
+ posixpath.join(generate_basename, '007.1_generate_eq_tpr'),
1112
+ logger=self.logger,
1113
+ BASENAME_002=self.stepnames[2],
1114
+ BASENAME_003=self.stepnames[3],
1115
+ BASENAME_004=self.stepnames[4],
1116
+ BASENAME_005=self.stepnames[5],
1117
+ BASENAME_006=self.stepnames[6],
1118
+ BOX_MODIFIED_GRO_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
1119
+ 'box_modified.gro')),
1120
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1121
+ MODIFIED_TOP_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
1122
+ 'solvated.top')),
1123
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1124
+ MODIFIED_GRO_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
1125
+ 'solvated.gro')),
1126
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1127
+ NEW_BOX_X_TEMPLATE=f'{new_box_x:.5f}',
1128
+ NEW_BOX_Y_TEMPLATE=f'{new_box_y:.5f}',
1129
+ NEW_BOX_Z_TEMPLATE=f'{new_box_z:.5f}',
1130
+ MIN_MDP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
1131
+ '007_Minimize.mdp')),
1132
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1133
+ MDP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
1134
+ '007_Equilibration.mdp')),
1135
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1136
+ GRO_FILE_TEMPLATE=os.path.relpath(os.path.abspath(self.structureFile),
1137
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1138
+ TOP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(self.topologyFile),
1139
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1140
+ COLVARS_INPUT_TEMPLATE=os.path.relpath(os.path.abspath(colvars_inputfile_basename_eq + '.dat'),
1141
+ os.path.abspath(generate_basename)).replace('\\', '/'))
1142
+ # generate shell script for free-energy calculation
1143
+ generateShellScript(pkg_resources.read_text(templates_gromacs, '007.generate_tpr_sh.template'),
1144
+ posixpath.join(generate_basename, '007.2_generate_tpr'),
1145
+ logger=self.logger,
1146
+ BASENAME_002=self.stepnames[2],
1147
+ BASENAME_003=self.stepnames[3],
1148
+ BASENAME_004=self.stepnames[4],
1149
+ BASENAME_005=self.stepnames[5],
1150
+ BASENAME_006=self.stepnames[6],
1151
+ MDP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
1152
+ '007_PMF.mdp')),
1153
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1154
+ GRO_FILE_TEMPLATE=os.path.relpath(os.path.abspath(f'{self.baseDirectory}/007_r/output/007_r_eq.out.gro'),
1155
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1156
+ TOP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(f'{self.baseDirectory}/007_r/solvated.top'),
1157
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1158
+ COLVARS_INPUT_TEMPLATE=os.path.relpath(os.path.abspath(colvars_inputfile_basename + '.dat'),
1159
+ os.path.abspath(generate_basename)).replace('\\', '/'))
1160
+ # also copy the awk script to modify the colvars configuration according to the PMF minima in previous stages
1161
+ with pkg_resources.path(templates_gromacs, 'find_min_max.awk') as p:
1162
+ shutil.copyfile(p, posixpath.join(generate_basename, 'find_min_max.awk'))
1163
+ if not posixpath.exists(posixpath.join(generate_basename, 'output')):
1164
+ os.makedirs(posixpath.join(generate_basename, 'output'))
1165
+ self.logger.info(f"Generation of {generate_basename} done.")
1166
+ self.logger.info('=' * 80)
1167
+
1168
+ def generate008(self):
1169
+ """generate files for determining the PMF along the RMSD of the ligand
1170
+ with respect to its unbound state
1171
+ """
1172
+
1173
+ self.handler.setFormatter(logging.Formatter('%(asctime)s [BFEEGromacs][008][%(levelname)s]:%(message)s'))
1174
+ generate_basename = self.basenames[8]
1175
+ self.logger.info('=' * 80)
1176
+ self.logger.info(f'Generating simulation files for {generate_basename}...')
1177
+ if not posixpath.exists(generate_basename):
1178
+ self.logger.info(f'Making directory {os.path.abspath(generate_basename)}...')
1179
+ os.makedirs(generate_basename)
1180
+ colvars_inputfile_basename_eq = posixpath.join(generate_basename, '008_eq_colvars')
1181
+ colvars_inputfile_basename = posixpath.join(generate_basename, '008_colvars')
1182
+ # generate the MDP file for equlibration
1183
+ generateMDP(pkg_resources.read_text(templates_gromacs, '008.mdp.template'),
1184
+ posixpath.join(generate_basename, '008_Equilibration'),
1185
+ logger=self.logger,
1186
+ timeStep=0.002,
1187
+ numSteps=1000000,
1188
+ temperature=self.temperature,
1189
+ pressure=1.01325,
1190
+ colvarsFile='008_eq_colvars.dat')
1191
+ # generate the MDP file
1192
+ generateMDP(pkg_resources.read_text(templates_gromacs, '008.mdp.template'),
1193
+ posixpath.join(generate_basename, '008_PMF'),
1194
+ logger=self.logger,
1195
+ timeStep=0.002,
1196
+ numSteps=4000000,
1197
+ temperature=self.temperature,
1198
+ pressure=1.01325,
1199
+ colvarsFile='008_colvars.dat')
1200
+ # generate the index file
1201
+ if hasattr(self, 'ligandOnly'):
1202
+ self.ligandOnly.write(posixpath.join(generate_basename, 'colvars_ligand_only.ndx'), name='BFEE_Ligand_Only')
1203
+ # colvars file for equilibration
1204
+ generateColvars(pkg_resources.read_text(templates_gromacs, '008_eq.colvars.template'),
1205
+ colvars_inputfile_basename_eq,
1206
+ ligand_selection='BFEE_Ligand_Only',
1207
+ logger=self.logger)
1208
+ # generate the colvars configuration
1209
+ generateColvars(pkg_resources.read_text(templates_gromacs, '008.colvars.template'),
1210
+ colvars_inputfile_basename,
1211
+ rmsd_bin_width=0.005,
1212
+ rmsd_lower_boundary=0.0,
1213
+ rmsd_upper_boundary=0.5,
1214
+ rmsd_wall_constant=0.8368,
1215
+ ligand_selection='BFEE_Ligand_Only',
1216
+ logger=self.logger)
1217
+ # generate the reference file for ligand only
1218
+ # extract the positions from the host-guest binding system
1219
+ ligand_position_in_system = self.ligand.positions
1220
+ # modify positions in the ligand-only system
1221
+ self.ligandOnly.positions = ligand_position_in_system
1222
+ # write out the whole ligand-only system as reference
1223
+ self.ligandOnlySystem.select_atoms('all').write(posixpath.join(generate_basename, 'reference_ligand_only.xyz'))
1224
+ # generate the shell script for making the tpr file for equilibration
1225
+ generateShellScript(pkg_resources.read_text(templates_gromacs, '008_eq.generate_tpr_sh.template'),
1226
+ posixpath.join(generate_basename, '008.1_generate_eq_tpr'),
1227
+ logger=self.logger,
1228
+ MDP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
1229
+ '008_Equilibration.mdp')),
1230
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1231
+ GRO_FILE_TEMPLATE=os.path.relpath(os.path.abspath(self.ligandOnlyStructureFile),
1232
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1233
+ TOP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(self.ligandOnlyTopologyFile),
1234
+ os.path.abspath(generate_basename)).replace('\\', '/'),)
1235
+ # generate the shell script for making the tpr file for free-energy calculation
1236
+ generateShellScript(pkg_resources.read_text(templates_gromacs, '008.generate_tpr_sh.template'),
1237
+ posixpath.join(generate_basename, '008.2_generate_tpr'),
1238
+ logger=self.logger,
1239
+ MDP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(posixpath.join(generate_basename,
1240
+ '008_PMF.mdp')),
1241
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1242
+ GRO_FILE_TEMPLATE=os.path.relpath(os.path.abspath(f'{self.baseDirectory}/008_RMSD_unbound/output/008_RMSD_unbound_eq.out.gro'),
1243
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1244
+ TOP_FILE_TEMPLATE=os.path.relpath(os.path.abspath(self.ligandOnlyTopologyFile),
1245
+ os.path.abspath(generate_basename)).replace('\\', '/'),
1246
+ COLVARS_INPUT_TEMPLATE=os.path.relpath(os.path.abspath(colvars_inputfile_basename + '.dat'),
1247
+ os.path.abspath(generate_basename)).replace('\\', '/'))
1248
+ if not posixpath.exists(posixpath.join(generate_basename, 'output')):
1249
+ os.makedirs(posixpath.join(generate_basename, 'output'))
1250
+ self.logger.info(f"Generation of {generate_basename} done.")
1251
+ self.logger.info('=' * 80)
1252
+
1253
+
1254
+ if __name__ == "__main__":
1255
+ bfee = BFEEGromacs('p41-abl.pdb', 'p41-abl.top', 'ligand-only.pdb', 'ligand-only.top', 'p41-abl-test/abc/def')
1256
+ bfee.setProteinHeavyAtomsGroup('segid SH3D and not (name H*)')
1257
+ bfee.setLigandHeavyAtomsGroup('segid PPRO and not (name H*)')
1258
+ bfee.setSolventAtomsGroup('resname TIP3*')
1259
+ bfee.setTemperature(350.0)
1260
+ bfee.generate000()
1261
+ bfee.generate001()
1262
+ bfee.generate002()
1263
+ bfee.generate003()
1264
+ bfee.generate004()
1265
+ bfee.generate005()
1266
+ bfee.generate006()
1267
+ bfee.generate007()
1268
+ bfee.generate008()