1 #!/bin/env python
2 #
3 # File: PyMOLVisualizeSurfaceAndBuriedResidues.py
4 # Author: Manish Sud <msud@san.rr.com>
5 #
6 # Copyright (C) 2026 Manish Sud. All rights reserved.
7 #
8 # The functionality available in this script is implemented using PyMOL, a
9 # molecular visualization system on an open source foundation originally
10 # developed by Warren DeLano.
11 #
12 # This file is part of MayaChemTools.
13 #
14 # MayaChemTools is free software; you can redistribute it and/or modify it under
15 # the terms of the GNU Lesser General Public License as published by the Free
16 # Software Foundation; either version 3 of the License, or (at your option) any
17 # later version.
18 #
19 # MayaChemTools is distributed in the hope that it will be useful, but without
20 # any warranty; without even the implied warranty of merchantability of fitness
21 # for a particular purpose. See the GNU Lesser General Public License for more
22 # details.
23 #
24 # You should have received a copy of the GNU Lesser General Public License
25 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
26 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
27 # Boston, MA, 02111-1307, USA.
28 #
29
30 from __future__ import print_function
31
32 import os
33 import sys
34 import time
35 import re
36
37 # PyMOL imports...
38 try:
39 import pymol
40
41 # Finish launching PyMOL in a command line mode for batch processing (-c)
42 # along with the following options: disable loading of pymolrc and plugins (-k);
43 # suppress start up messages (-q)
44 pymol.finish_launching(["pymol", "-ckq"])
45 except ImportError as ErrMsg:
46 sys.stderr.write("\nFailed to import PyMOL module/package: %s\n" % ErrMsg)
47 sys.stderr.write("Check/update your PyMOL environment and try again.\n\n")
48 sys.exit(1)
49
50 # MayaChemTools imports...
51 sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), "..", "lib", "Python"))
52 try:
53 from docopt import docopt
54 import MiscUtil
55 import PyMOLUtil
56 except ImportError as ErrMsg:
57 sys.stderr.write("\nFailed to import MayaChemTools module/package: %s\n" % ErrMsg)
58 sys.stderr.write("Check/update your MayaChemTools environment and try again.\n\n")
59 sys.exit(1)
60
61 ScriptName = os.path.basename(sys.argv[0])
62 Options = {}
63 OptionsInfo = {}
64
65
66 def main():
67 """Start execution of the script."""
68
69 MiscUtil.PrintInfo(
70 "\n%s (PyMOL v%s; MayaChemTools v%s; %s): Starting...\n"
71 % (ScriptName, pymol.cmd.get_version()[0], MiscUtil.GetMayaChemToolsVersion(), time.asctime())
72 )
73
74 (WallClockTime, ProcessorTime) = MiscUtil.GetWallClockAndProcessorTime()
75
76 # Retrieve command line arguments and options...
77 RetrieveOptions()
78
79 # Process and validate command line arguments and options...
80 ProcessOptions()
81
82 # Perform actions required by the script...
83 GenerateSurfaceAndBuriedResiduesVisualization()
84
85 MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName)
86 MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime))
87
88
89 def GenerateSurfaceAndBuriedResiduesVisualization():
90 """Generate visualization for surface and buried residues."""
91
92 Outfile = OptionsInfo["PMLOutfile"]
93 OutFH = open(Outfile, "w")
94 if OutFH is None:
95 MiscUtil.PrintError("Failed to open output fie %s " % Outfile)
96
97 MiscUtil.PrintInfo("\nGenerating file %s..." % Outfile)
98
99 # Setup header...
100 WritePMLHeader(OutFH, ScriptName)
101 WritePyMOLParameters(OutFH)
102
103 # Load reffile for alignment..
104 if OptionsInfo["Align"]:
105 WriteAlignReference(OutFH)
106
107 # Setup view for each input file...
108 FirstComplex = True
109 FirstComplexFirstChainName = None
110 for FileIndex in range(0, len(OptionsInfo["InfilesInfo"]["InfilesNames"])):
111 # Setup PyMOL object names...
112 PyMOLObjectNames = SetupPyMOLObjectNames(FileIndex)
113
114 # Setup complex view...
115 WriteComplexView(OutFH, FileIndex, PyMOLObjectNames, FirstComplex)
116
117 SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
118 FirstChain = True
119 for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
120 if FirstComplex and FirstChain:
121 FirstComplexFirstChainName = PyMOLObjectNames["Chains"][ChainID]["ChainAlone"]
122
123 WriteChainView(OutFH, FileIndex, PyMOLObjectNames, ChainID)
124
125 # Setup ligand views...
126 FirstLigand = True
127 for LigandID in SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID]:
128 WriteChainLigandView(OutFH, FileIndex, PyMOLObjectNames, ChainID, LigandID)
129
130 # Set up ligand level group...
131 Enable, Action = [False, "close"]
132 if FirstLigand:
133 FirstLigand = False
134 Enable, Action = [True, "open"]
135 GenerateAndWritePMLForGroup(
136 OutFH,
137 PyMOLObjectNames["Ligands"][ChainID][LigandID]["ChainLigandGroup"],
138 PyMOLObjectNames["Ligands"][ChainID][LigandID]["ChainLigandGroupMembers"],
139 Enable,
140 Action,
141 )
142
143 # Setup Chain level group...
144 Enable, Action = [False, "close"]
145 if FirstChain:
146 FirstChain = False
147 Enable, Action = [True, "open"]
148 GenerateAndWritePMLForGroup(
149 OutFH,
150 PyMOLObjectNames["Chains"][ChainID]["ChainGroup"],
151 PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"],
152 Enable,
153 Action,
154 )
155
156 # Set up complex level group...
157 Enable, Action = [False, "close"]
158 if FirstComplex:
159 FirstComplex = False
160 Enable, Action = [True, "open"]
161 GenerateAndWritePMLForGroup(
162 OutFH, PyMOLObjectNames["PDBGroup"], PyMOLObjectNames["PDBGroupMembers"], Enable, Action
163 )
164
165 # Delete empty PyMOL objects...
166 DeleteEmptyPyMOLObjects(OutFH, FileIndex, PyMOLObjectNames)
167
168 if OptionsInfo["Align"]:
169 DeleteAlignReference(OutFH)
170
171 if FirstComplexFirstChainName is not None:
172 OutFH.write("""\ncmd.orient("%s", animate = -1)\n""" % FirstComplexFirstChainName)
173 else:
174 OutFH.write("""\ncmd.orient("visible", animate = -1)\n""")
175
176 OutFH.close()
177
178 # Generate PSE file as needed...
179 if OptionsInfo["PSEOut"]:
180 GeneratePyMOLSessionFile()
181
182
183 def WritePMLHeader(OutFH, ScriptName):
184 """Write out PML setting up complex view."""
185
186 HeaderInfo = PyMOLUtil.SetupPMLHeaderInfo(ScriptName)
187 OutFH.write("%s\n" % HeaderInfo)
188
189
190 def WritePyMOLParameters(OutFH):
191 """Write out PyMOL global parameters."""
192
193 PMLCmds = []
194 PMLCmds.append("""cmd.set("transparency", %.2f, "", 0)""" % (OptionsInfo["SurfaceTransparency"]))
195 PMLCmds.append("""cmd.set("label_font_id", %s)""" % (OptionsInfo["LabelFontID"]))
196
197 PML = "\n".join(PMLCmds)
198
199 OutFH.write("""\n""\n"Setting up PyMOL gobal parameters..."\n""\n""")
200 OutFH.write("%s\n" % PML)
201
202
203 def WriteAlignReference(OutFH):
204 """Setup object for alignment reference."""
205
206 RefFileInfo = OptionsInfo["RefFileInfo"]
207 RefFile = RefFileInfo["RefFileName"]
208 RefName = RefFileInfo["PyMOLObjectName"]
209
210 PMLCmds = []
211 PMLCmds.append("""cmd.load("%s", "%s")""" % (RefFile, RefName))
212 PMLCmds.append("""cmd.hide("everything", "%s")""" % (RefName))
213 PMLCmds.append("""cmd.disable("%s")""" % (RefName))
214 PML = "\n".join(PMLCmds)
215
216 OutFH.write("""\n""\n"Loading %s and setting up view for align reference..."\n""\n""" % RefFile)
217 OutFH.write("%s\n" % PML)
218
219
220 def WriteAlignComplex(OutFH, FileIndex, PyMOLObjectNames):
221 """Setup alignment of complex to reference."""
222
223 RefFileInfo = OptionsInfo["RefFileInfo"]
224 RefName = RefFileInfo["PyMOLObjectName"]
225
226 ComplexName = PyMOLObjectNames["Complex"]
227
228 if re.match("^FirstChain$", OptionsInfo["AlignMode"], re.I):
229 RefFirstChainID = RefFileInfo["ChainsAndLigandsInfo"]["ChainIDs"][0]
230 RefAlignSelection = "%s and chain %s" % (RefName, RefFirstChainID)
231
232 ComplexFirstChainID = RetrieveFirstChainID(FileIndex)
233 ComplexAlignSelection = "%s and chain %s" % (ComplexName, ComplexFirstChainID)
234 else:
235 RefAlignSelection = RefName
236 ComplexAlignSelection = ComplexName
237
238 PML = PyMOLUtil.SetupPMLForAlignment(OptionsInfo["AlignMethod"], RefAlignSelection, ComplexAlignSelection)
239 OutFH.write("""\n""\n"Aligning %s against reference %s ..."\n""\n""" % (ComplexAlignSelection, RefAlignSelection))
240 OutFH.write("%s\n" % PML)
241
242
243 def DeleteAlignReference(OutFH):
244 """Delete alignment reference object."""
245
246 RefName = OptionsInfo["RefFileInfo"]["PyMOLObjectName"]
247 OutFH.write("""\n""\n"Deleting alignment reference object %s..."\n""\n""" % RefName)
248 OutFH.write("""cmd.delete("%s")\n""" % RefName)
249
250
251 def WriteComplexView(OutFH, FileIndex, PyMOLObjectNames, FirstComplex):
252 """Write out PML for viewing polymer complex."""
253
254 # Setup complex...
255 Infile = OptionsInfo["InfilesInfo"]["InfilesNames"][FileIndex]
256 PML = PyMOLUtil.SetupPMLForPolymerComplexView(PyMOLObjectNames["Complex"], Infile, True)
257 OutFH.write("""\n""\n"Loading %s and setting up view for complex..."\n""\n""" % Infile)
258 OutFH.write("%s\n" % PML)
259
260 if OptionsInfo["Align"]:
261 # No need to align complex on to itself...
262 if not (re.match("^FirstInputFile$", OptionsInfo["AlignRefFile"], re.I) and FirstComplex):
263 WriteAlignComplex(OutFH, FileIndex, PyMOLObjectNames)
264
265 if OptionsInfo["SurfaceComplex"]:
266 # Setup hydrophobic surface...
267 PML = PyMOLUtil.SetupPMLForHydrophobicSurfaceView(
268 PyMOLObjectNames["ComplexHydrophobicSurface"],
269 PyMOLObjectNames["Complex"],
270 ColorPalette=OptionsInfo["SurfaceColorPalette"],
271 Enable=False,
272 )
273 OutFH.write("\n%s\n" % PML)
274
275 # Setup complex group...
276 GenerateAndWritePMLForGroup(
277 OutFH, PyMOLObjectNames["ComplexGroup"], PyMOLObjectNames["ComplexGroupMembers"], False, "close"
278 )
279
280
281 def WriteChainView(OutFH, FileIndex, PyMOLObjectNames, ChainID):
282 """Write out PML for viewing chain."""
283
284 OutFH.write("""\n""\n"Setting up views for chain %s..."\n""\n""" % ChainID)
285
286 ChainComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
287
288 # Setup chain complex group view...
289 WriteChainComplexViews(OutFH, FileIndex, PyMOLObjectNames, ChainID)
290
291 # Setup chain view...
292 WriteChainAloneViews(OutFH, FileIndex, PyMOLObjectNames, ChainID)
293
294 # Setup chain solvent view...
295 PML = PyMOLUtil.SetupPMLForSolventView(PyMOLObjectNames["Chains"][ChainID]["Solvent"], ChainComplexName, False)
296 OutFH.write("\n%s\n" % PML)
297
298 # Setup chain inorganic view...
299 PML = PyMOLUtil.SetupPMLForInorganicView(PyMOLObjectNames["Chains"][ChainID]["Inorganic"], ChainComplexName, False)
300 OutFH.write("\n%s\n" % PML)
301
302
303 def WriteChainComplexViews(OutFH, FileIndex, PyMOLObjectNames, ChainID):
304 """Write chain complex views."""
305
306 # Setup chain complex...
307 ChainComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
308 PML = PyMOLUtil.SetupPMLForPolymerChainComplexView(ChainComplexName, PyMOLObjectNames["Complex"], ChainID, True)
309 OutFH.write("%s\n" % PML)
310
311 if OptionsInfo["SurfaceChainComplex"]:
312 # Setup hydrophobic surface...
313 PML = PyMOLUtil.SetupPMLForHydrophobicSurfaceView(
314 PyMOLObjectNames["Chains"][ChainID]["ChainComplexHydrophobicSurface"],
315 ChainComplexName,
316 ColorPalette=OptionsInfo["SurfaceColorPalette"],
317 Enable=False,
318 )
319 OutFH.write("\n%s\n" % PML)
320
321 # Setup chain complex group...
322 GenerateAndWritePMLForGroup(
323 OutFH,
324 PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroup"],
325 PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"],
326 False,
327 "close",
328 )
329
330
331 def WriteChainAloneViews(OutFH, FileIndex, PyMOLObjectNames, ChainID):
332 """Write individual chain views."""
333
334 ChainComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
335
336 # Setup a chain view...
337 ChainName = PyMOLObjectNames["Chains"][ChainID]["ChainAlone"]
338 PML = PyMOLUtil.SetupPMLForPolymerChainView(ChainName, ChainComplexName, True)
339 OutFH.write("\n%s\n" % PML)
340
341 ChainAloneGroupID = "ChainAloneGroup"
342
343 for GroupType in ["Surface_Residues", "Buried_Residues"]:
344 GroupID = re.sub("_", "", GroupType)
345
346 ResiduesGroupID = "%s%sGroup" % (ChainAloneGroupID, GroupID)
347 ResiduesGroupMembersID = "%sMembers" % (ResiduesGroupID)
348
349 # Setup a chain view using residues selection...
350 ResiduesChainNameID = "%sChain" % (ResiduesGroupID)
351 ResiduesChainName = PyMOLObjectNames["Chains"][ChainID][ResiduesChainNameID]
352 ChainResiduesSelection = GetChainResiduesSelection(FileIndex, ChainID, GroupType)
353
354 Selection = "%s and chain %s and polymer and (%s)" % (ChainComplexName, ChainID, ChainResiduesSelection)
355 PML = PyMOLUtil.SetupPMLForSelectionDisplayView(ResiduesChainName, Selection, "cartoon", Enable=False)
356 OutFH.write("\n%s\n" % PML)
357
358 WriteChainAloneResiduesGroupResiduesTypesView(
359 GroupType, OutFH, FileIndex, PyMOLObjectNames, ChainID, ResiduesGroupID
360 )
361 WriteChainAloneResiduesGroupResiduesSurfacesView(
362 GroupType, OutFH, FileIndex, PyMOLObjectNames, ChainID, ResiduesGroupID
363 )
364
365 # Setup residues group...
366 GenerateAndWritePMLForGroup(
367 OutFH,
368 PyMOLObjectNames["Chains"][ChainID][ResiduesGroupID],
369 PyMOLObjectNames["Chains"][ChainID][ResiduesGroupMembersID],
370 True,
371 "open",
372 )
373
374 # Setup chain group...
375 GenerateAndWritePMLForGroup(
376 OutFH,
377 PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroup"],
378 PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"],
379 True,
380 "open",
381 )
382
383
384 def WriteChainLigandView(OutFH, FileIndex, PyMOLObjectNames, ChainID, LigandID):
385 """Write out PML for viewing ligand in a chain."""
386
387 GroupID = "Ligand"
388 ComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
389
390 # Setup main object...
391 GroupTypeObjectID = "%s" % (GroupID)
392 GroupTypeObjectName = PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupTypeObjectID]
393
394 OutFH.write("""\n""\n"Setting up views for ligand %s in chain %s..."\n""\n""" % (LigandID, ChainID))
395 PML = PyMOLUtil.SetupPMLForLigandView(
396 GroupTypeObjectName, ComplexName, LigandID, Enable=True, IgnoreHydrogens=OptionsInfo["IgnoreHydrogens"]
397 )
398 OutFH.write("%s\n" % PML)
399
400 # Setup ball and stick view...
401 BallAndStickNameID = "%sBallAndStick" % (GroupID)
402 BallAndStickName = PyMOLObjectNames["Ligands"][ChainID][LigandID][BallAndStickNameID]
403 PML = PyMOLUtil.SetupPMLForBallAndStickView(BallAndStickName, GroupTypeObjectName, Enable=False)
404 OutFH.write("\n%s\n" % PML)
405
406 # Setup group....
407 GroupNameID = "%sGroup" % (GroupID)
408 GroupMembersID = "%sGroupMembers" % (GroupID)
409 GroupName = PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupNameID]
410 GroupMembers = PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID]
411
412 Action = "close"
413 Enable = True
414 GenerateAndWritePMLForGroup(OutFH, GroupName, GroupMembers, Enable, Action)
415
416
417 def GetChainResiduesSelection(FileIndex, ChainID, GroupType):
418 """Get residue selection for surface or buried residues for a chain."""
419
420 SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
421 ResiduesSelection = (
422 SpecifiedChainsAndLigandsInfo["SurfaceResiduesSelection"][ChainID]
423 if re.match("^Surface_Residues$", GroupType, re.I)
424 else SpecifiedChainsAndLigandsInfo["BuriedResiduesSelection"][ChainID]
425 )
426
427 return ResiduesSelection
428
429
430 def WriteChainAloneResiduesGroupResiduesTypesView(GroupType, OutFH, FileIndex, PyMOLObjectNames, ChainID, MainGroupID):
431 """Write out PML for viewing residue types for surface or buried residues in a chain."""
432
433 if not GetChainAloneResidueTypesStatus(FileIndex, ChainID):
434 return
435
436 NameID = "%sChain" % (MainGroupID)
437 ChainName = PyMOLObjectNames["Chains"][ChainID][NameID]
438
439 ResiduesGroupID = "%sResiduesGroup" % MainGroupID
440 ResiduesGroupMembersID = "%sMembers" % ResiduesGroupID
441
442 # Setup residue types objects...
443 for SubGroupType in ["Aromatic", "Hydrophobic", "Polar", "Positively_Charged", "Negatively_Charged", "Other"]:
444 SubGroupID = re.sub("_", "", SubGroupType)
445
446 ResiduesSubGroupID = "%s%sGroup" % (ResiduesGroupID, SubGroupID)
447 ResiduesSubGroupMembersID = "%sMembers" % (ResiduesSubGroupID)
448
449 ResiduesObjectID = "%sResidues" % (ResiduesSubGroupID)
450 ResiduesObjectName = PyMOLObjectNames["Chains"][ChainID][ResiduesObjectID]
451
452 ResiduesSurfaceObjectID = "%sSurface" % (ResiduesSubGroupID)
453 ResiduesSurfaceObjectName = PyMOLObjectNames["Chains"][ChainID][ResiduesSurfaceObjectID]
454
455 ResiduesColor = OptionsInfo["ResidueTypesParams"][SubGroupType]["Color"]
456 ResiduesNames = OptionsInfo["ResidueTypesParams"][SubGroupType]["Residues"]
457
458 NegateResidueNames = True if re.match("^Other$", SubGroupType, re.I) else False
459 WriteResidueTypesResiduesAndSurfaceView(
460 OutFH,
461 ChainName,
462 ResiduesObjectName,
463 ResiduesSurfaceObjectName,
464 ResiduesColor,
465 ResiduesNames,
466 NegateResidueNames,
467 )
468
469 # Setup sub groups for residue types..
470 GenerateAndWritePMLForGroup(
471 OutFH,
472 PyMOLObjectNames["Chains"][ChainID][ResiduesSubGroupID],
473 PyMOLObjectNames["Chains"][ChainID][ResiduesSubGroupMembersID],
474 True,
475 "close",
476 )
477
478 # Setup residues group...
479 GenerateAndWritePMLForGroup(
480 OutFH,
481 PyMOLObjectNames["Chains"][ChainID][ResiduesGroupID],
482 PyMOLObjectNames["Chains"][ChainID][ResiduesGroupMembersID],
483 False,
484 "close",
485 )
486
487
488 def WriteResidueTypesResiduesAndSurfaceView(
489 OutFH, SelectionObjectName, Name, SurfaceName, ResiduesColor, ResiduesNames, NegateResidueNames
490 ):
491 """Write residue types residues and surface view."""
492
493 ResidueNamesSelection = "+".join(ResiduesNames)
494 if NegateResidueNames:
495 Selection = "%s and (not resn %s)" % (SelectionObjectName, ResidueNamesSelection)
496 else:
497 Selection = "%s and (resn %s)" % (SelectionObjectName, ResidueNamesSelection)
498
499 # Setup residues...
500 PML = PyMOLUtil.SetupPMLForSelectionDisplayView(Name, Selection, "lines", ResiduesColor, True)
501 OutFH.write("\n%s\n" % PML)
502
503 # Setup surface...
504 PML = PyMOLUtil.SetupPMLForSelectionDisplayView(SurfaceName, Selection, "surface", ResiduesColor, True)
505 OutFH.write("\n%s\n" % PML)
506
507
508 def WriteChainAloneResiduesGroupResiduesSurfacesView(
509 GroupType, OutFH, FileIndex, PyMOLObjectNames, ChainID, MainGroupID
510 ):
511 """Write out PML for viewing surfaces for surface and buried residues in chains."""
512
513 if not GetChainAloneContainsSurfacesStatus(FileIndex, ChainID):
514 return
515
516 NameID = "%sChain" % (MainGroupID)
517 ChainName = PyMOLObjectNames["Chains"][ChainID][NameID]
518
519 SurfacesGroupID = "%sSurfaceGroup" % MainGroupID
520 SurfacesGroupMembersID = "%sMembers" % SurfacesGroupID
521
522 ProcessingBuriedResidues = True if re.match("^Buried_Residues$", GroupType, re.I) else False
523
524 # Setup a generic color surface...
525 SurfaceID = "%sSurface" % (SurfacesGroupID)
526 SurfaceName = PyMOLObjectNames["Chains"][ChainID][SurfaceID]
527 ColorName = OptionsInfo["SurfaceBuriedResiduesColor"] if ProcessingBuriedResidues else OptionsInfo["SurfaceColor"]
528 PML = PyMOLUtil.SetupPMLForSurfaceView(SurfaceName, ChainName, Enable=True, DisplayAs=None, Color=ColorName)
529 OutFH.write("\n%s\n" % PML)
530
531 if GetChainAloneSurfaceChainStatus(FileIndex, ChainID):
532 # Setup surface colored by hydrophobicity...
533 HydrophobicSurfaceID = "%sHydrophobicSurface" % (SurfacesGroupID)
534 HydrophobicSurfaceName = PyMOLObjectNames["Chains"][ChainID][HydrophobicSurfaceID]
535 PML = PyMOLUtil.SetupPMLForHydrophobicSurfaceView(
536 HydrophobicSurfaceName,
537 ChainName,
538 ColorPalette=OptionsInfo["SurfaceColorPalette"],
539 Enable=False,
540 DisplayAs=None,
541 )
542 OutFH.write("\n%s\n" % PML)
543
544 # Setup surface colored by hyrdophobicity and charge...
545 HydrophobicChargeSurfaceID = "%sHydrophobicChargeSurface" % (SurfacesGroupID)
546 HydrophobicChargeSurfaceName = PyMOLObjectNames["Chains"][ChainID][HydrophobicChargeSurfaceID]
547 PML = PyMOLUtil.SetupPMLForHydrophobicAndChargeSurfaceView(
548 HydrophobicChargeSurfaceName,
549 ChainName,
550 OptionsInfo["AtomTypesColorNames"]["HydrophobicAtomsColor"],
551 OptionsInfo["AtomTypesColorNames"]["NegativelyChargedAtomsColor"],
552 OptionsInfo["AtomTypesColorNames"]["PositivelyChargedAtomsColor"],
553 OptionsInfo["AtomTypesColorNames"]["OtherAtomsColor"],
554 Enable=False,
555 DisplayAs=None,
556 )
557 OutFH.write("\n%s\n" % PML)
558
559 if GetChainAloneSurfaceChainElectrostaticsStatus(FileIndex, ChainID):
560 # Setup electrostatics surface...
561 ElectrostaticsGroupID = "%sElectrostaticsGroup" % (SurfacesGroupID)
562 ElectrostaticsGroupMembersID = "%sMembers" % (ElectrostaticsGroupID)
563 ElectrostaticsGroupName = PyMOLObjectNames["Chains"][ChainID][ElectrostaticsGroupID]
564 ElectrostaticsGroupMembers = PyMOLObjectNames["Chains"][ChainID][ElectrostaticsGroupMembersID]
565 WriteSurfaceElectrostaticsView(
566 OutFH, ChainName, ElectrostaticsGroupName, ElectrostaticsGroupMembers, DisplayAs=None
567 )
568
569 # Setup surfaces group...
570 GenerateAndWritePMLForGroup(
571 OutFH,
572 PyMOLObjectNames["Chains"][ChainID][SurfacesGroupID],
573 PyMOLObjectNames["Chains"][ChainID][SurfacesGroupMembersID],
574 True,
575 "close",
576 )
577
578
579 def WriteSurfaceElectrostaticsView(
580 OutFH, SelectionObjectName, ElectrostaticsGroupName, ElectrostaticsGroupMembers, DisplayAs="lines"
581 ):
582 """Write out PML for viewing surface electrostatics."""
583
584 if len(ElectrostaticsGroupMembers) == 5:
585 Name, ContactPotentialName, MapName, LegendName, VolumeName = ElectrostaticsGroupMembers
586 else:
587 Name, ContactPotentialName, MapName, LegendName = ElectrostaticsGroupMembers
588 VolumeName = None
589
590 PMLCmds = []
591
592 # Setup chain...
593 PMLCmds.append("""cmd.create("%s", "(%s)")""" % (Name, SelectionObjectName))
594
595 # Setup vacuum electrostatics surface along with associated objects...
596 PMLCmds.append("""util.protein_vacuum_esp("%s", mode=2, quiet=0, _self=cmd)""" % (Name))
597 PMLCmds.append("""cmd.set_name("%s_e_chg", "%s")""" % (Name, ContactPotentialName))
598
599 if DisplayAs is not None:
600 PMLCmds.append("""cmd.show("%s", "(%s)")""" % (DisplayAs, ContactPotentialName))
601
602 PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(ContactPotentialName, Enable=True))
603
604 PMLCmds.append("""cmd.set_name("%s_e_map", "%s")""" % (Name, MapName))
605 PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(MapName, Enable=False))
606
607 PMLCmds.append("""cmd.set_name("%s_e_pot", "%s")""" % (Name, LegendName))
608 PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(LegendName, Enable=False))
609
610 if VolumeName is not None:
611 PMLCmds.append("""cmd.volume("%s", "%s", "%s", "(%s)")""" % (VolumeName, MapName, "esp", Name))
612 PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(VolumeName, Enable=False))
613
614 # Delete name and take it out from the group membership. It is
615 # is already part of ContactPotential object.
616 PMLCmds.append("""cmd.delete("%s")""" % (Name))
617 ElectrostaticsGroupMembers.pop(0)
618
619 PML = "\n".join(PMLCmds)
620
621 OutFH.write("\n%s\n" % PML)
622
623 # Setup group...
624 GenerateAndWritePMLForGroup(OutFH, ElectrostaticsGroupName, ElectrostaticsGroupMembers, False, "close")
625
626
627 def GenerateAndWritePMLForGroup(OutFH, GroupName, GroupMembers, Enable=False, Action="close"):
628 """Generate and write PML for group."""
629
630 PML = PyMOLUtil.SetupPMLForGroup(GroupName, GroupMembers, Enable, Action)
631 OutFH.write("""\n""\n"Setting up group %s..."\n""\n""" % GroupName)
632 OutFH.write("%s\n" % PML)
633
634
635 def GeneratePyMOLSessionFile():
636 """Generate PME file from PML file."""
637
638 PSEOutfile = OptionsInfo["PSEOutfile"]
639 PMLOutfile = OptionsInfo["PMLOutfile"]
640
641 MiscUtil.PrintInfo("\nGenerating file %s..." % PSEOutfile)
642
643 PyMOLUtil.ConvertPMLFileToPSEFile(PMLOutfile, PSEOutfile)
644
645 if not os.path.exists(PSEOutfile):
646 MiscUtil.PrintWarning("Failed to generate PSE file, %s..." % (PSEOutfile))
647
648 if not OptionsInfo["PMLOut"]:
649 MiscUtil.PrintInfo("Deleting file %s..." % PMLOutfile)
650 os.remove(PMLOutfile)
651
652
653 def DeleteEmptyPyMOLObjects(OutFH, FileIndex, PyMOLObjectNames):
654 """Delete empty PyMOL objects."""
655
656 if OptionsInfo["AllowEmptyObjects"]:
657 return
658
659 SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
660 for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
661 OutFH.write("""\n""\n"Checking and deleting empty objects for chain %s..."\n""\n""" % (ChainID))
662
663 # Delete any chain level objects...
664 WritePMLToCheckAndDeleteEmptyObjects(OutFH, PyMOLObjectNames["Chains"][ChainID]["Solvent"])
665 WritePMLToCheckAndDeleteEmptyObjects(OutFH, PyMOLObjectNames["Chains"][ChainID]["Inorganic"])
666
667 # Delete any chain alone level objects
668 ChainAloneGroupID = "ChainAloneGroup"
669
670 for GroupType in ["Surface_Residues", "Buried_Residues"]:
671 GroupID = re.sub("_", "", GroupType)
672 ResiduesGroupID = "%s%sGroup" % (ChainAloneGroupID, GroupID)
673
674 # Delete empty chain object...
675 ResiduesChainNameID = "%sChain" % (ResiduesGroupID)
676 WritePMLToCheckAndDeleteEmptyObjects(OutFH, PyMOLObjectNames["Chains"][ChainID][ResiduesChainNameID])
677
678 # Delete empty residue type objects...
679 DeleteEmptyChainAloneResiduesGroupResiduesTypesObjects(
680 GroupType, OutFH, FileIndex, PyMOLObjectNames, ChainID, ResiduesGroupID
681 )
682
683 # Delete empty surface objects...
684 DeleteEmptyAloneResiduesGroupResiduesSurfacesObjects(
685 GroupType, OutFH, FileIndex, PyMOLObjectNames, ChainID, ResiduesGroupID
686 )
687
688
689 def DeleteEmptyChainAloneResiduesGroupResiduesTypesObjects(
690 GroupType, OutFH, FileIndex, PyMOLObjectNames, ChainID, MainGroupID
691 ):
692 """Delete empty residue type objects for surface or buried residues in a chain."""
693
694 if not GetChainAloneResidueTypesStatus(FileIndex, ChainID):
695 return
696
697 ResiduesGroupID = "%sResiduesGroup" % MainGroupID
698 for SubGroupType in ["Aromatic", "Hydrophobic", "Polar", "Positively_Charged", "Negatively_Charged", "Other"]:
699 SubGroupID = re.sub("_", "", SubGroupType)
700
701 ResiduesSubGroupID = "%s%sGroup" % (ResiduesGroupID, SubGroupID)
702 SubGroupName = PyMOLObjectNames["Chains"][ChainID][ResiduesSubGroupID]
703
704 SubGroupObjectNamesList = []
705
706 ResiduesObjectID = "%sResidues" % (ResiduesSubGroupID)
707 ResiduesObjectName = PyMOLObjectNames["Chains"][ChainID][ResiduesObjectID]
708 SubGroupObjectNamesList.append(ResiduesObjectName)
709
710 ResiduesSurfaceObjectID = "%sSurface" % (ResiduesSubGroupID)
711 ResiduesSurfaceObjectName = PyMOLObjectNames["Chains"][ChainID][ResiduesSurfaceObjectID]
712 SubGroupObjectNamesList.append(ResiduesSurfaceObjectName)
713
714 SubGroupObjectNames = ",".join(SubGroupObjectNamesList)
715 WritePMLToCheckAndDeleteEmptyObjects(OutFH, SubGroupObjectNames, SubGroupName)
716
717 # Delete residues group object...
718 DeleteResiduesGroup = (
719 False if AreGroupTypeResiduesPresent(GroupType, FileIndex, PyMOLObjectNames, ChainID) else True
720 )
721 if DeleteResiduesGroup:
722 OutFH.write("""cmd.delete("%s")\n""" % PyMOLObjectNames["Chains"][ChainID][ResiduesGroupID])
723
724
725 def DeleteEmptyAloneResiduesGroupResiduesSurfacesObjects(
726 GroupType, OutFH, FileIndex, PyMOLObjectNames, ChainID, MainGroupID
727 ):
728 """Delete empty surfaces objects for surface or buried residues in a chain."""
729
730 if not GetChainAloneResidueTypesStatus(FileIndex, ChainID):
731 return
732
733 if AreGroupTypeResiduesPresent(GroupType, FileIndex, PyMOLObjectNames, ChainID):
734 return
735
736 SurfacesGroupID = "%sSurfaceGroup" % MainGroupID
737
738 # Delete plain surface...
739 SurfaceID = "%sSurface" % (SurfacesGroupID)
740 WritePMLToCheckAndDeleteEmptyObjects(OutFH, PyMOLObjectNames["Chains"][ChainID][SurfaceID])
741
742 if GetChainAloneSurfaceChainStatus(FileIndex, ChainID):
743 HydrophobicSurfaceID = "%sHydrophobicSurface" % (SurfacesGroupID)
744 WritePMLToCheckAndDeleteEmptyObjects(OutFH, PyMOLObjectNames["Chains"][ChainID][HydrophobicSurfaceID])
745
746 HydrophobicChargeSurfaceID = "%sHydrophobicChargeSurface" % (SurfacesGroupID)
747 WritePMLToCheckAndDeleteEmptyObjects(OutFH, PyMOLObjectNames["Chains"][ChainID][HydrophobicChargeSurfaceID])
748
749 if GetChainAloneSurfaceChainElectrostaticsStatus(FileIndex, ChainID):
750 ElectrostaticsGroupID = "%sElectrostaticsGroup" % (SurfacesGroupID)
751 ElectrostaticsGroupMembersID = "%sMembers" % (ElectrostaticsGroupID)
752
753 ElectrostaticsGroupName = PyMOLObjectNames["Chains"][ChainID][ElectrostaticsGroupID]
754 ElectrostaticsGroupMembers = PyMOLObjectNames["Chains"][ChainID][ElectrostaticsGroupMembersID]
755 ElectrostaticsGroupMembersNames = ",".join(ElectrostaticsGroupMembers)
756 WritePMLToCheckAndDeleteEmptyObjects(OutFH, ElectrostaticsGroupMembersNames, ElectrostaticsGroupName)
757
758 # Delete surface group...
759 OutFH.write("""cmd.delete("%s")\n""" % PyMOLObjectNames["Chains"][ChainID][SurfacesGroupID])
760
761
762 def AreGroupTypeResiduesPresent(GroupType, FileIndex, PyMOLObjectNames, ChainID):
763 """Check presence of surface or buries residue groups in a chain."""
764
765 GroupTypeResiduesPresent = True
766
767 SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
768
769 if re.match("^Surface_Residues$", GroupType, re.I):
770 GroupTypeResiduesPresent = SpecifiedChainsAndLigandsInfo["SurfaceResiduesPresent"][ChainID]
771 elif re.match("^Buried_Residues$", GroupType, re.I):
772 GroupTypeResiduesPresent = SpecifiedChainsAndLigandsInfo["BuriedResiduesPresent"][ChainID]
773
774 return GroupTypeResiduesPresent
775
776
777 def WritePMLToCheckAndDeleteEmptyObjects(OutFH, ObjectName, ParentObjectName=None):
778 """Write PML to check and delete empty PyMOL objects."""
779
780 if ParentObjectName is None:
781 PML = """CheckAndDeleteEmptyObjects("%s")""" % (ObjectName)
782 else:
783 PML = """CheckAndDeleteEmptyObjects("%s", "%s")""" % (ObjectName, ParentObjectName)
784
785 OutFH.write("%s\n" % PML)
786
787
788 def SetupPyMOLObjectNames(FileIndex):
789 """Setup hierarchy of PyMOL groups and objects for ligand centric views of
790 chains and ligands present in input file.
791 """
792
793 PyMOLObjectNames = {}
794 PyMOLObjectNames["Chains"] = {}
795 PyMOLObjectNames["Ligands"] = {}
796
797 # Setup groups and objects for complex...
798 SetupPyMOLObjectNamesForComplex(FileIndex, PyMOLObjectNames)
799
800 # Setup groups and objects for chain...
801 SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
802 for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
803 SetupPyMOLObjectNamesForChain(FileIndex, PyMOLObjectNames, ChainID)
804
805 # Setup groups and objects for ligand...
806 for LigandID in SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID]:
807 SetupPyMOLObjectNamesForLigand(FileIndex, PyMOLObjectNames, ChainID, LigandID)
808
809 return PyMOLObjectNames
810
811
812 def SetupPyMOLObjectNamesForComplex(FileIndex, PyMOLObjectNames):
813 """Setup groups and objects for complex."""
814
815 PDBFileRoot = OptionsInfo["InfilesInfo"]["InfilesRoots"][FileIndex]
816
817 PDBGroupName = "%s" % PDBFileRoot
818 PyMOLObjectNames["PDBGroup"] = PDBGroupName
819 PyMOLObjectNames["PDBGroupMembers"] = []
820
821 ComplexGroupName = "%s.Complex" % PyMOLObjectNames["PDBGroup"]
822 PyMOLObjectNames["ComplexGroup"] = ComplexGroupName
823 PyMOLObjectNames["PDBGroupMembers"].append(ComplexGroupName)
824
825 PyMOLObjectNames["Complex"] = "%s.Complex" % ComplexGroupName
826 if OptionsInfo["SurfaceComplex"]:
827 PyMOLObjectNames["ComplexHydrophobicSurface"] = "%s.Surface" % ComplexGroupName
828
829 PyMOLObjectNames["ComplexGroupMembers"] = []
830 PyMOLObjectNames["ComplexGroupMembers"].append(PyMOLObjectNames["Complex"])
831 if OptionsInfo["SurfaceComplex"]:
832 PyMOLObjectNames["ComplexGroupMembers"].append(PyMOLObjectNames["ComplexHydrophobicSurface"])
833
834
835 def SetupPyMOLObjectNamesForChain(FileIndex, PyMOLObjectNames, ChainID):
836 """Setup groups and objects for chain."""
837
838 PDBGroupName = PyMOLObjectNames["PDBGroup"]
839
840 PyMOLObjectNames["Chains"][ChainID] = {}
841 PyMOLObjectNames["Ligands"][ChainID] = {}
842
843 # Set up chain group and chain objects...
844 ChainGroupName = "%s.Chain%s" % (PDBGroupName, ChainID)
845 PyMOLObjectNames["Chains"][ChainID]["ChainGroup"] = ChainGroupName
846 PyMOLObjectNames["PDBGroupMembers"].append(ChainGroupName)
847 PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"] = []
848
849 # Setup chain complex group and objects...
850 ChainComplexGroupName = "%s.Complex" % (ChainGroupName)
851 PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroup"] = ChainComplexGroupName
852 PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(ChainComplexGroupName)
853
854 PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"] = []
855
856 Name = "%s.Complex" % (ChainComplexGroupName)
857 PyMOLObjectNames["Chains"][ChainID]["ChainComplex"] = Name
858 PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"].append(Name)
859
860 if OptionsInfo["SurfaceChainComplex"]:
861 Name = "%s.Surface" % (ChainComplexGroupName)
862 PyMOLObjectNames["Chains"][ChainID]["ChainComplexHydrophobicSurface"] = Name
863 PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"].append(Name)
864
865 # Setup up a group for individual chains...
866 ChainAloneGroupName = "%s.Chain" % (ChainGroupName)
867 PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroup"] = ChainAloneGroupName
868 PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(ChainAloneGroupName)
869
870 PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"] = []
871
872 Name = "%s.Chain" % (ChainAloneGroupName)
873 PyMOLObjectNames["Chains"][ChainID]["ChainAlone"] = Name
874 PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"].append(Name)
875
876 # Setup groups and their members for surface and buried residues...
877 ChainAloneGroupID = "ChainAloneGroup"
878 for GroupType in ["Surface_Residues", "Buried_Residues"]:
879 GroupID = re.sub("_", "", GroupType)
880
881 ResiduesGroupName = "%s.%s" % (ChainAloneGroupName, GroupType)
882 ResiduesGroupID = "%s%sGroup" % (ChainAloneGroupID, GroupID)
883 ResiduesGroupMembersID = "%sMembers" % (ResiduesGroupID)
884
885 PyMOLObjectNames["Chains"][ChainID][ResiduesGroupID] = ResiduesGroupName
886 PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"].append(ResiduesGroupName)
887
888 PyMOLObjectNames["Chains"][ChainID][ResiduesGroupMembersID] = []
889
890 # Add a chain for surface and buried residues...
891 Name = "%s.Chain" % (ResiduesGroupName)
892 NameID = "%sChain" % (ResiduesGroupID)
893 PyMOLObjectNames["Chains"][ChainID][NameID] = Name
894 PyMOLObjectNames["Chains"][ChainID][ResiduesGroupMembersID].append(Name)
895
896 SetupPyMOLObjectNamesForChainResiduesTypes(
897 FileIndex, PyMOLObjectNames, ChainID, ResiduesGroupName, ResiduesGroupID, ResiduesGroupMembersID
898 )
899 SetupPyMOLObjectNamesForChainResiduesSurfaces(
900 FileIndex, PyMOLObjectNames, ChainID, ResiduesGroupName, ResiduesGroupID, ResiduesGroupMembersID
901 )
902
903 # Setup solvent and inorganic objects for chain...
904 for NameID in ["Solvent", "Inorganic"]:
905 Name = "%s.%s" % (ChainGroupName, NameID)
906 PyMOLObjectNames["Chains"][ChainID][NameID] = Name
907 PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(Name)
908
909
910 def SetupPyMOLObjectNamesForChainResiduesTypes(
911 FileIndex, PyMOLObjectNames, ChainID, MainGroupName, MainGroupID, MainGroupMembersID
912 ):
913 """Setup residue type groups and its objects for residues."""
914
915 if not GetChainAloneResidueTypesStatus(FileIndex, ChainID):
916 return
917
918 ResiduesGroupName = "%s.Residues" % (MainGroupName)
919 ResiduesGroupID = "%sResiduesGroup" % MainGroupID
920 ResiduesGroupMembersID = "%sMembers" % ResiduesGroupID
921
922 # Add residue type group to residues group...
923 PyMOLObjectNames["Chains"][ChainID][ResiduesGroupID] = ResiduesGroupName
924 PyMOLObjectNames["Chains"][ChainID][MainGroupMembersID].append(ResiduesGroupName)
925
926 # Initialize residue type group members...
927 PyMOLObjectNames["Chains"][ChainID][ResiduesGroupMembersID] = []
928
929 # Setup residues sub groups and its members...
930 for SubGroupType in ["Aromatic", "Hydrophobic", "Polar", "Positively_Charged", "Negatively_Charged", "Other"]:
931 SubGroupID = re.sub("_", "", SubGroupType)
932
933 ResiduesSubGroupName = "%s.%s" % (ResiduesGroupName, SubGroupType)
934 ResiduesSubGroupID = "%s%sGroup" % (ResiduesGroupID, SubGroupID)
935 ResiduesSubGroupMembersID = "%sMembers" % (ResiduesSubGroupID)
936
937 # Add sub group to residues group...
938 PyMOLObjectNames["Chains"][ChainID][ResiduesSubGroupID] = ResiduesSubGroupName
939 PyMOLObjectNames["Chains"][ChainID][ResiduesGroupMembersID].append(ResiduesSubGroupName)
940
941 # Initialize sub group members...
942 PyMOLObjectNames["Chains"][ChainID][ResiduesSubGroupMembersID] = []
943
944 # Add sub group members to subgroup...
945 for MemberType in ["Residues", "Surface"]:
946 MemberID = re.sub("_", "", MemberType)
947
948 SubGroupMemberName = "%s.%s" % (ResiduesSubGroupName, MemberType)
949 SubGroupMemberID = "%s%s" % (ResiduesSubGroupID, MemberID)
950
951 PyMOLObjectNames["Chains"][ChainID][SubGroupMemberID] = SubGroupMemberName
952 PyMOLObjectNames["Chains"][ChainID][ResiduesSubGroupMembersID].append(SubGroupMemberName)
953
954
955 def SetupPyMOLObjectNamesForChainResiduesSurfaces(
956 FileIndex, PyMOLObjectNames, ChainID, MainGroupName, MainGroupID, MainGroupMembersID
957 ):
958 """Setup residue surface groups and its objects for residues."""
959
960 if not GetChainAloneContainsSurfacesStatus(FileIndex, ChainID):
961 return
962
963 SubGroupName = "%s.Surface" % (MainGroupName)
964 SubGroupID = "%sSurfaceGroup" % MainGroupID
965 SubGroupMembersID = "%sMembers" % SubGroupID
966
967 # Add surface group to main group...
968 PyMOLObjectNames["Chains"][ChainID][SubGroupID] = SubGroupName
969 PyMOLObjectNames["Chains"][ChainID][MainGroupMembersID].append(SubGroupName)
970
971 # Initialize surface group members...
972 PyMOLObjectNames["Chains"][ChainID][SubGroupMembersID] = []
973
974 # Setup a generic color surface...
975 SurfaceName = "%s.Surface" % (SubGroupName)
976 SurfaceID = "%sSurface" % (SubGroupID)
977 PyMOLObjectNames["Chains"][ChainID][SurfaceID] = SurfaceName
978 PyMOLObjectNames["Chains"][ChainID][SubGroupMembersID].append(SurfaceName)
979
980 if GetChainAloneSurfaceChainStatus(FileIndex, ChainID):
981 # Setup hydrophobicity surface...
982 HydrophobicSurfaceName = "%s.Hydrophobicity" % (SubGroupName)
983 HydrophobicSurfaceID = "%sHydrophobicSurface" % (SubGroupID)
984 PyMOLObjectNames["Chains"][ChainID][HydrophobicSurfaceID] = HydrophobicSurfaceName
985 PyMOLObjectNames["Chains"][ChainID][SubGroupMembersID].append(HydrophobicSurfaceName)
986
987 # Setup hydrophobicity and charge surface...
988 HydrophobicChargeSurfaceName = "%s.Hydrophobicity_Charge" % (SubGroupName)
989 HydrophobicChargeSurfaceID = "%sHydrophobicChargeSurface" % (SubGroupID)
990 PyMOLObjectNames["Chains"][ChainID][HydrophobicChargeSurfaceID] = HydrophobicChargeSurfaceName
991 PyMOLObjectNames["Chains"][ChainID][SubGroupMembersID].append(HydrophobicChargeSurfaceName)
992
993 if GetChainAloneSurfaceChainElectrostaticsStatus(FileIndex, ChainID):
994 # Setup electrostatics group...
995 ElectrostaticsGroupName = "%s.Vacuum_Electrostatics" % (SubGroupName)
996 ElectrostaticsGroupID = "%sElectrostaticsGroup" % (SubGroupID)
997 ElectrostaticsGroupMembersID = "%sElectrostaticsGroupMembers" % (SubGroupID)
998
999 PyMOLObjectNames["Chains"][ChainID][ElectrostaticsGroupID] = ElectrostaticsGroupName
1000 PyMOLObjectNames["Chains"][ChainID][SubGroupMembersID].append(ElectrostaticsGroupName)
1001
1002 # Setup electrostatics group members...
1003 PyMOLObjectNames["Chains"][ChainID][ElectrostaticsGroupMembersID] = []
1004
1005 for MemberType in ["Chain", "Contact_Potential", "Map", "Legend"]:
1006 MemberID = re.sub("_", "", MemberType)
1007
1008 Name = "%s.%s" % (ElectrostaticsGroupName, MemberType)
1009 NameID = "%s%s" % (ElectrostaticsGroupID, MemberID)
1010
1011 PyMOLObjectNames["Chains"][ChainID][NameID] = Name
1012 PyMOLObjectNames["Chains"][ChainID][ElectrostaticsGroupMembersID].append(Name)
1013
1014
1015 def SetupPyMOLObjectNamesForLigand(FileIndex, PyMOLObjectNames, ChainID, LigandID):
1016 """Stetup groups and objects for ligand."""
1017
1018 PyMOLObjectNames["Ligands"][ChainID][LigandID] = {}
1019
1020 ChainGroupName = PyMOLObjectNames["Chains"][ChainID]["ChainGroup"]
1021
1022 # Setup a chain level ligand group...
1023 ChainLigandGroupName = "%s.Ligand%s" % (ChainGroupName, LigandID)
1024 PyMOLObjectNames["Ligands"][ChainID][LigandID]["ChainLigandGroup"] = ChainLigandGroupName
1025 PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(ChainLigandGroupName)
1026
1027 PyMOLObjectNames["Ligands"][ChainID][LigandID]["ChainLigandGroupMembers"] = []
1028
1029 # Set up ligand group and its members...
1030 GroupName = "%s.Ligand" % (ChainLigandGroupName)
1031 GroupNameID = "LigandGroup"
1032 GroupMembersID = "LigandGroupMembers"
1033
1034 PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupNameID] = GroupName
1035 PyMOLObjectNames["Ligands"][ChainID][LigandID]["ChainLigandGroupMembers"].append(GroupName)
1036
1037 LigandName = "%s.Ligand" % (GroupName)
1038 LigandNameID = "Ligand"
1039 PyMOLObjectNames["Ligands"][ChainID][LigandID][LigandNameID] = LigandName
1040
1041 PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID] = []
1042 PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID].append(LigandName)
1043
1044 # Add ball and stick...
1045 BallAndStickName = "%s.BallAndStick" % (GroupName)
1046 BallAndStickID = "LigandBallAndStick"
1047 PyMOLObjectNames["Ligands"][ChainID][LigandID][BallAndStickID] = BallAndStickName
1048 PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID].append(BallAndStickName)
1049
1050
1051 def RetrieveInfilesInfo():
1052 """Retrieve information for input files."""
1053
1054 InfilesInfo = {}
1055
1056 InfilesInfo["InfilesNames"] = []
1057 InfilesInfo["InfilesRoots"] = []
1058 InfilesInfo["ChainsAndLigandsInfo"] = []
1059
1060 for Infile in OptionsInfo["InfilesNames"]:
1061 FileDir, FileName, FileExt = MiscUtil.ParseFileName(Infile)
1062 InfileRoot = FileName
1063
1064 ChainsAndLigandInfo = PyMOLUtil.GetChainsAndLigandsInfo(Infile, InfileRoot)
1065
1066 InfilesInfo["InfilesNames"].append(Infile)
1067 InfilesInfo["InfilesRoots"].append(InfileRoot)
1068 InfilesInfo["ChainsAndLigandsInfo"].append(ChainsAndLigandInfo)
1069
1070 OptionsInfo["InfilesInfo"] = InfilesInfo
1071
1072
1073 def RetrieveRefFileInfo():
1074 """Retrieve information for ref file."""
1075
1076 RefFileInfo = {}
1077 if not OptionsInfo["Align"]:
1078 OptionsInfo["RefFileInfo"] = RefFileInfo
1079 return
1080
1081 RefFile = OptionsInfo["RefFileName"]
1082
1083 FileDir, FileName, FileExt = MiscUtil.ParseFileName(RefFile)
1084 RefFileRoot = FileName
1085
1086 if re.match("^FirstInputFile$", OptionsInfo["AlignRefFile"], re.I):
1087 ChainsAndLigandInfo = OptionsInfo["InfilesInfo"]["ChainsAndLigandsInfo"][0]
1088 else:
1089 MiscUtil.PrintInfo("\nRetrieving chain and ligand information for alignment reference file %s..." % RefFile)
1090 ChainsAndLigandInfo = PyMOLUtil.GetChainsAndLigandsInfo(RefFile, RefFileRoot)
1091
1092 RefFileInfo["RefFileName"] = RefFile
1093 RefFileInfo["RefFileRoot"] = RefFileRoot
1094 RefFileInfo["PyMOLObjectName"] = "AlignRef_%s" % RefFileRoot
1095 RefFileInfo["ChainsAndLigandsInfo"] = ChainsAndLigandInfo
1096
1097 OptionsInfo["RefFileInfo"] = RefFileInfo
1098
1099
1100 def ProcessChainAndLigandIDs():
1101 """Process specified chain and ligand IDs for infiles."""
1102
1103 OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"] = []
1104
1105 for FileIndex in range(0, len(OptionsInfo["InfilesInfo"]["InfilesNames"])):
1106 MiscUtil.PrintInfo(
1107 "\nProcessing specified chain and ligand IDs for input file %s..."
1108 % OptionsInfo["InfilesInfo"]["InfilesNames"][FileIndex]
1109 )
1110
1111 ChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["ChainsAndLigandsInfo"][FileIndex]
1112 SpecifiedChainsAndLigandsInfo = PyMOLUtil.ProcessChainsAndLigandsOptionsInfo(
1113 ChainsAndLigandsInfo, "-c, --chainIDs", OptionsInfo["ChainIDs"], "-l, --ligandIDs", OptionsInfo["LigandIDs"]
1114 )
1115 ProcessResidueTypesAndSurfaceOptions(FileIndex, SpecifiedChainsAndLigandsInfo)
1116 OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"].append(SpecifiedChainsAndLigandsInfo)
1117
1118 CheckPresenceOfValidLigandIDs(ChainsAndLigandsInfo, SpecifiedChainsAndLigandsInfo)
1119
1120
1121 def RetrieveSurfaceAndBuriedResiduesInfo():
1122 """Setup surface and buried residues for specified chains."""
1123
1124 for FileIndex in range(0, len(OptionsInfo["InfilesInfo"]["InfilesNames"])):
1125 Infile = OptionsInfo["InfilesInfo"]["InfilesNames"][FileIndex]
1126 MolName = OptionsInfo["InfilesInfo"]["InfilesRoots"][FileIndex]
1127
1128 MiscUtil.PrintInfo("\nRetrieving surface and buried residues from input file %s..." % Infile)
1129
1130 # Load infile...
1131 pymol.cmd.load(Infile, MolName)
1132
1133 SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
1134
1135 # Initialize surface and buried residues selection...
1136 SpecifiedChainsAndLigandsInfo["SurfaceResiduesPresent"] = {}
1137 SpecifiedChainsAndLigandsInfo["SurfaceResiduesSelection"] = {}
1138 SpecifiedChainsAndLigandsInfo["BuriedResiduesSelection"] = {}
1139 SpecifiedChainsAndLigandsInfo["BuriedResiduesPresent"] = {}
1140
1141 # Go over all specified chains...
1142 for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
1143 SurfaceResiduesInfo, BuriedResiduesInfo = PyMOLUtil.GetSurfaceAndBuriedResiduesInfo(
1144 MolName, ChainID, OptionsInfo["CutoffSASA"]
1145 )
1146
1147 # Retrieve surface and buried residue numbers...
1148 SurfaceResiduesNums = RetrieveResiduesNumbers(SurfaceResiduesInfo)
1149 BuriedResiduesNums = RetrieveResiduesNumbers(BuriedResiduesInfo)
1150
1151 # Setup PyMOL selection for surface and buried residue numbers...
1152 SurfaceResiduesSelection, BuriedResiduesSelection = SetupSurfaceAndBuriedResiduesSelection(
1153 SurfaceResiduesNums, BuriedResiduesNums
1154 )
1155
1156 # Track surface and buried residues...
1157 SpecifiedChainsAndLigandsInfo["SurfaceResiduesPresent"][ChainID] = (
1158 True if len(SurfaceResiduesNums) else False
1159 )
1160 SpecifiedChainsAndLigandsInfo["SurfaceResiduesSelection"][ChainID] = SurfaceResiduesSelection
1161
1162 SpecifiedChainsAndLigandsInfo["BuriedResiduesPresent"][ChainID] = True if len(BuriedResiduesNums) else False
1163 SpecifiedChainsAndLigandsInfo["BuriedResiduesSelection"][ChainID] = BuriedResiduesSelection
1164
1165 # List surface residues...
1166 SurfaceResiduesCount, SurfaceResiduesDistribution, SurfaceResiduesIDs = FormatResiduesInfo(
1167 SurfaceResiduesInfo
1168 )
1169 MiscUtil.PrintInfo(
1170 "\nInput file: %s; ChainID: %s\nNumber of surface residues: %d"
1171 % (Infile, ChainID, SurfaceResiduesCount)
1172 )
1173 MiscUtil.PrintInfo("Residue distribution: %s" % (SurfaceResiduesDistribution))
1174 if OptionsInfo["ResidueIDs"]:
1175 MiscUtil.PrintInfo("Residue IDs: %s" % (SurfaceResiduesIDs))
1176
1177 # List buried residues...
1178 BuriedResiduesCount, BuriedResiduesDistribution, BuriedResiduesIDs = FormatResiduesInfo(BuriedResiduesInfo)
1179 MiscUtil.PrintInfo(
1180 "\nInput file: %s;ChainID: %s\nNumber of buried residues: %d" % (Infile, ChainID, BuriedResiduesCount)
1181 )
1182 MiscUtil.PrintInfo("Residue distribution: %s" % (BuriedResiduesDistribution))
1183 if OptionsInfo["ResidueIDs"]:
1184 MiscUtil.PrintInfo("Residue IDs: %s" % (BuriedResiduesIDs))
1185
1186 # Delete loaded object...
1187 pymol.cmd.delete(MolName)
1188
1189
1190 def RetrieveResiduesNumbers(ResiduesInfo):
1191 """Retrieve residue numbers."""
1192
1193 # Setup residue IDs sorted by residue numbers...
1194 ResNumMap = {}
1195 for ResName in ResiduesInfo["ResNames"]:
1196 for ResNum in ResiduesInfo["ResNum"][ResName]:
1197 ResNumMap[ResNum] = ResName
1198
1199 ResNumsList = []
1200 if len(ResNumMap):
1201 ResNumsList = sorted(ResNumMap, key=int)
1202
1203 return ResNumsList
1204
1205
1206 def SetupSurfaceAndBuriedResiduesSelection(SurfaceResiduesNums, BuriedResiduesNums):
1207 """Setup PyMOL selection for surface and buried residues."""
1208
1209 SurfaceResiduesSelection, BuriedResiduesSelection = [None] * 2
1210 UseSurfaceResidueNums, UseBuriedResidueNums = [False] * 2
1211
1212 SurfaceResiduesCount = len(SurfaceResiduesNums)
1213 BuriedResiduesCount = len(BuriedResiduesNums)
1214
1215 # Setup selections using the residue list containing lower number of residues for
1216 # ease of read in PML file...
1217 if SurfaceResiduesCount and BuriedResiduesCount:
1218 if SurfaceResiduesCount < BuriedResiduesCount:
1219 UseSurfaceResidueNums = True
1220 else:
1221 UseBuriedResidueNums = True
1222 elif SurfaceResiduesCount:
1223 UseSurfaceResidueNums = True
1224 elif BuriedResiduesCount:
1225 UseBuriedResidueNums = True
1226
1227 if UseSurfaceResidueNums:
1228 SurfaceResiduesSelection = "resi %s" % ("+".join(SurfaceResiduesNums))
1229 BuriedResiduesSelection = "not %s" % SurfaceResiduesSelection
1230 elif UseBuriedResidueNums:
1231 BuriedResiduesSelection = "resi %s" % ("+".join(BuriedResiduesNums))
1232 SurfaceResiduesSelection = "not %s" % BuriedResiduesSelection
1233
1234 return SurfaceResiduesSelection, BuriedResiduesSelection
1235
1236
1237 def FormatResiduesInfo(SelectionInfo):
1238 """Format residues info."""
1239
1240 # Setup distribution of residues...
1241 LineWords = []
1242 ResiduesCount = 0
1243 SortedResNames = sorted(
1244 SelectionInfo["ResNames"], key=lambda ResName: SelectionInfo["ResCount"][ResName], reverse=True
1245 )
1246 for ResName in SortedResNames:
1247 ResCount = SelectionInfo["ResCount"][ResName]
1248 LineWords.append("%s - %s" % (ResName, ResCount))
1249 ResiduesCount += ResCount
1250
1251 ResiduesDistribution = "; ".join(LineWords) if len(LineWords) else None
1252
1253 # Setup residue IDs sorted by residue numbers...
1254 ResNumMap = {}
1255 for ResName in SelectionInfo["ResNames"]:
1256 for ResNum in SelectionInfo["ResNum"][ResName]:
1257 ResNumMap[ResNum] = ResName
1258
1259 ResNumsList = []
1260 if len(ResNumMap):
1261 ResNumsList = sorted(ResNumMap, key=int)
1262
1263 LineWords = []
1264 for ResNum in ResNumsList:
1265 ResName = ResNumMap[ResNum]
1266 ResID = "%s_%s" % (ResName, ResNum)
1267 LineWords.append(ResID)
1268 ResiduesIDs = ", ".join(LineWords) if len(LineWords) else None
1269
1270 return ResiduesCount, ResiduesDistribution, ResiduesIDs
1271
1272
1273 def ProcessResidueTypesAndSurfaceOptions(FileIndex, SpecifiedChainsAndLigandsInfo):
1274 """Process residue types and surface options for chains."""
1275
1276 SpecifiedChainsAndLigandsInfo["ChainSurfaces"] = {}
1277 SpecifiedChainsAndLigandsInfo["SurfaceChain"] = {}
1278 SpecifiedChainsAndLigandsInfo["SurfaceChainElectrostatics"] = {}
1279
1280 SpecifiedChainsAndLigandsInfo["ResidueTypesChain"] = {}
1281
1282 # Load infile...
1283 Infile = OptionsInfo["InfilesInfo"]["InfilesNames"][FileIndex]
1284 MolName = OptionsInfo["InfilesInfo"]["InfilesRoots"][FileIndex]
1285 pymol.cmd.load(Infile, MolName)
1286
1287 for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
1288 AminoAcidsPresent = PyMOLUtil.AreAminoAcidResiduesPresent(MolName, ChainID)
1289
1290 # Process surfaces for chains...
1291 if re.match("^auto$", OptionsInfo["SurfaceChain"], re.I):
1292 SurfaceChain = True if AminoAcidsPresent else False
1293 else:
1294 SurfaceChain = True if re.match("^yes$", OptionsInfo["SurfaceChain"], re.I) else False
1295 SpecifiedChainsAndLigandsInfo["SurfaceChain"][ChainID] = SurfaceChain
1296
1297 if re.match("^auto$", OptionsInfo["SurfaceChainElectrostatics"], re.I):
1298 SurfaceChainElectrostatics = True if AminoAcidsPresent else False
1299 else:
1300 SurfaceChainElectrostatics = (
1301 True if re.match("^yes$", OptionsInfo["SurfaceChainElectrostatics"], re.I) else False
1302 )
1303 SpecifiedChainsAndLigandsInfo["SurfaceChainElectrostatics"][ChainID] = SurfaceChainElectrostatics
1304
1305 # A generic color surface is always created...
1306 ChainSurfaces = True
1307 SpecifiedChainsAndLigandsInfo["ChainSurfaces"][ChainID] = ChainSurfaces
1308
1309 # Process residue types for chains...
1310 if re.match("^auto$", OptionsInfo["ResidueTypesChain"], re.I):
1311 ResidueTypesChain = True if AminoAcidsPresent else False
1312 else:
1313 ResidueTypesChain = True if re.match("^yes$", OptionsInfo["ResidueTypesChain"], re.I) else False
1314 SpecifiedChainsAndLigandsInfo["ResidueTypesChain"][ChainID] = ResidueTypesChain
1315
1316 # Delete loaded object...
1317 pymol.cmd.delete(MolName)
1318
1319
1320 def GetChainAloneResidueTypesStatus(FileIndex, ChainID):
1321 """Get status of residue types for chain alone object."""
1322
1323 # o Need to handle Surface_Residues and Buried residues group based detection...
1324 Status = (
1325 True
1326 if OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]["ResidueTypesChain"][ChainID]
1327 else False
1328 )
1329
1330 return Status
1331
1332
1333 def GetChainAloneContainsSurfacesStatus(FileIndex, ChainID):
1334 """Get status of surfaces present in chain alone object."""
1335
1336 return OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]["ChainSurfaces"][ChainID]
1337
1338
1339 def GetChainAloneSurfaceChainStatus(FileIndex, ChainID):
1340 """Get status of hydrophobic surfaces for chain alone object."""
1341
1342 return OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]["SurfaceChain"][ChainID]
1343
1344
1345 def GetChainAloneSurfaceChainElectrostaticsStatus(FileIndex, ChainID):
1346 """Get status of electrostatics surfaces for chain alone object."""
1347
1348 return OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]["SurfaceChainElectrostatics"][ChainID]
1349
1350
1351 def CheckPresenceOfValidLigandIDs(ChainsAndLigandsInfo, SpecifiedChainsAndLigandsInfo):
1352 """Check presence of valid ligand IDs."""
1353
1354 MiscUtil.PrintInfo("\nSpecified chain IDs: %s" % (", ".join(SpecifiedChainsAndLigandsInfo["ChainIDs"])))
1355
1356 for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
1357 if len(SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID]):
1358 MiscUtil.PrintInfo(
1359 "Chain ID: %s; Specified LigandIDs: %s"
1360 % (ChainID, ", ".join(SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID]))
1361 )
1362 else:
1363 MiscUtil.PrintInfo("Chain IDs: %s; Specified LigandIDs: None" % (ChainID))
1364 MiscUtil.PrintWarning(
1365 "No valid ligand IDs found for chain ID, %s. PyMOL groups and objects related to ligand and binding pockect won't be created."
1366 % (ChainID)
1367 )
1368
1369
1370 def RetrieveFirstChainID(FileIndex):
1371 """Get first chain ID."""
1372
1373 ChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["ChainsAndLigandsInfo"][FileIndex]
1374
1375 FirstChainID = None
1376 if len(ChainsAndLigandsInfo["ChainIDs"]):
1377 FirstChainID = ChainsAndLigandsInfo["ChainIDs"][0]
1378
1379 return FirstChainID
1380
1381
1382 def ProcessResidueTypes():
1383 """Process residue types."""
1384
1385 ResidueTypesNamesInfo, ResidueTypesParamsInfo = PyMOLUtil.ProcessResidueTypesOptionsInfo(
1386 "-r, --residueTypes", OptionsInfo["ResidueTypes"]
1387 )
1388 OptionsInfo["ResidueTypesNames"] = ResidueTypesNamesInfo
1389 OptionsInfo["ResidueTypesParams"] = ResidueTypesParamsInfo
1390
1391
1392 def ProcessSurfaceAtomTypesColors():
1393 """Process surface atom types colors."""
1394
1395 AtomTypesColorNamesInfo = PyMOLUtil.ProcessSurfaceAtomTypesColorsOptionsInfo(
1396 "--surfaceAtomTypesColors", OptionsInfo["SurfaceAtomTypesColors"]
1397 )
1398 OptionsInfo["AtomTypesColorNames"] = AtomTypesColorNamesInfo
1399
1400
1401 def ProcessOptions():
1402 """Process and validate command line arguments and options."""
1403
1404 MiscUtil.PrintInfo("Processing options...")
1405
1406 # Validate options...
1407 ValidateOptions()
1408
1409 OptionsInfo["Align"] = True if re.match("^Yes$", Options["--align"], re.I) else False
1410 OptionsInfo["AlignMethod"] = Options["--alignMethod"].lower()
1411 OptionsInfo["AlignMode"] = Options["--alignMode"]
1412
1413 OptionsInfo["AllowEmptyObjects"] = True if re.match("^Yes$", Options["--allowEmptyObjects"], re.I) else False
1414
1415 OptionsInfo["CutoffSASA"] = float(Options["--cutoffSASA"])
1416
1417 OptionsInfo["Infiles"] = Options["--infiles"]
1418 OptionsInfo["InfilesNames"] = Options["--infileNames"]
1419
1420 OptionsInfo["AlignRefFile"] = Options["--alignRefFile"]
1421 if re.match("^FirstInputFile$", Options["--alignRefFile"], re.I):
1422 OptionsInfo["RefFileName"] = OptionsInfo["InfilesNames"][0]
1423 else:
1424 OptionsInfo["RefFileName"] = Options["--alignRefFile"]
1425
1426 OptionsInfo["IgnoreHydrogens"] = True if re.match("^Yes$", Options["--ignoreHydrogens"], re.I) else False
1427
1428 OptionsInfo["Overwrite"] = Options["--overwrite"]
1429 OptionsInfo["PMLOut"] = True if re.match("^Yes$", Options["--PMLOut"], re.I) else False
1430
1431 OptionsInfo["Outfile"] = Options["--outfile"]
1432 FileDir, FileName, FileExt = MiscUtil.ParseFileName(OptionsInfo["Outfile"])
1433 OptionsInfo["PSEOut"] = False
1434 if re.match("^pml$", FileExt, re.I):
1435 OptionsInfo["PMLOutfile"] = OptionsInfo["Outfile"]
1436 OptionsInfo["PMEOutfile"] = re.sub(".pml$", ".pme", OptionsInfo["Outfile"])
1437 elif re.match("^pse$", FileExt, re.I):
1438 OptionsInfo["PSEOut"] = True
1439 OptionsInfo["PSEOutfile"] = OptionsInfo["Outfile"]
1440 OptionsInfo["PMLOutfile"] = re.sub(".pse$", ".pml", OptionsInfo["Outfile"])
1441 if os.path.exists(OptionsInfo["PMLOutfile"]) and (not OptionsInfo["Overwrite"]):
1442 MiscUtil.PrintError(
1443 'The intermediate output file to be generated, %s, already exist. Use option "--ov" or "--overwrite" and try again.'
1444 % OptionsInfo["PMLOutfile"]
1445 )
1446
1447 OptionsInfo["LabelFontID"] = int(Options["--labelFontID"])
1448
1449 OptionsInfo["ResidueIDs"] = True if re.match("^Yes$", Options["--residueIDs"], re.I) else False
1450
1451 OptionsInfo["ResidueTypesChain"] = Options["--residueTypesChain"]
1452 OptionsInfo["ResidueTypes"] = Options["--residueTypes"]
1453 ProcessResidueTypes()
1454
1455 OptionsInfo["SurfaceChain"] = Options["--surfaceChain"]
1456 OptionsInfo["SurfaceChainElectrostatics"] = Options["--surfaceChainElectrostatics"]
1457
1458 OptionsInfo["SurfaceChainComplex"] = True if re.match("^Yes$", Options["--surfaceChainComplex"], re.I) else False
1459 OptionsInfo["SurfaceComplex"] = True if re.match("^Yes$", Options["--surfaceComplex"], re.I) else False
1460
1461 # Retrieve surface colors for generic surfaces..
1462 SurfaceColors = re.sub(" ", "", Options["--surfaceColors"])
1463 SurfaceColorsWords = SurfaceColors.split(",")
1464 if len(SurfaceColorsWords) != 2:
1465 MiscUtil.PrintError(
1466 'The number of comma delimited color names, %d, specified using "--surfaceColors" option, "%s", must be a 2.'
1467 % (len(SurfaceColorsWords), Options["--surfaceColors"])
1468 )
1469 OptionsInfo["SurfaceColors"] = SurfaceColors
1470 OptionsInfo["SurfaceColor"] = SurfaceColorsWords[0]
1471 OptionsInfo["SurfaceBuriedResiduesColor"] = SurfaceColorsWords[1]
1472
1473 OptionsInfo["SurfaceColorPalette"] = Options["--surfaceColorPalette"]
1474 OptionsInfo["SurfaceAtomTypesColors"] = Options["--surfaceAtomTypesColors"]
1475 ProcessSurfaceAtomTypesColors()
1476
1477 OptionsInfo["SurfaceTransparency"] = float(Options["--surfaceTransparency"])
1478
1479 RetrieveInfilesInfo()
1480 RetrieveRefFileInfo()
1481
1482 OptionsInfo["ChainIDs"] = Options["--chainIDs"]
1483 OptionsInfo["LigandIDs"] = Options["--ligandIDs"]
1484 ProcessChainAndLigandIDs()
1485
1486 RetrieveSurfaceAndBuriedResiduesInfo()
1487
1488
1489 def RetrieveOptions():
1490 """Retrieve command line arguments and options."""
1491
1492 # Get options...
1493 global Options
1494 Options = docopt(_docoptUsage_)
1495
1496 # Set current working directory to the specified directory...
1497 WorkingDir = Options["--workingdir"]
1498 if WorkingDir:
1499 os.chdir(WorkingDir)
1500
1501 # Handle examples option...
1502 if "--examples" in Options and Options["--examples"]:
1503 MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_))
1504 sys.exit(0)
1505
1506
1507 def ValidateOptions():
1508 """Validate option values."""
1509
1510 MiscUtil.ValidateOptionTextValue("--align", Options["--align"], "yes no")
1511 MiscUtil.ValidateOptionTextValue("--alignMethod", Options["--alignMethod"], "align cealign super")
1512 MiscUtil.ValidateOptionTextValue("--alignMode", Options["--alignMode"], "FirstChain Complex")
1513
1514 MiscUtil.ValidateOptionTextValue("--allowEmptyObjects", Options["--allowEmptyObjects"], "yes no")
1515
1516 MiscUtil.ValidateOptionFloatValue("--cutoffSASA", Options["--cutoffSASA"], {">": 0.0})
1517
1518 # Expand infiles to handle presence of multiple input files...
1519 InfileNames = MiscUtil.ExpandFileNames(Options["--infiles"], ",")
1520 if not len(InfileNames):
1521 MiscUtil.PrintError('No input files specified for "-i, --infiles" option')
1522
1523 # Validate file extensions...
1524 for Infile in InfileNames:
1525 MiscUtil.ValidateOptionFilePath("-i, --infiles", Infile)
1526 MiscUtil.ValidateOptionFileExt("-i, --infiles", Infile, "pdb cif")
1527 MiscUtil.ValidateOptionsDistinctFileNames("-i, --infiles", Infile, "-o, --outfile", Options["--outfile"])
1528 Options["--infileNames"] = InfileNames
1529
1530 MiscUtil.ValidateOptionFileExt("-o, --outfile", Options["--outfile"], "pml pse")
1531 MiscUtil.ValidateOptionsOutputFileOverwrite(
1532 "-o, --outfile", Options["--outfile"], "--overwrite", Options["--overwrite"]
1533 )
1534
1535 if re.match("^yes$", Options["--align"], re.I):
1536 if not re.match("^FirstInputFile$", Options["--alignRefFile"], re.I):
1537 AlignRefFile = Options["--alignRefFile"]
1538 MiscUtil.ValidateOptionFilePath("--alignRefFile", AlignRefFile)
1539 MiscUtil.ValidateOptionFileExt("--alignRefFile", AlignRefFile, "pdb cif")
1540 MiscUtil.ValidateOptionsDistinctFileNames(
1541 "--AlignRefFile", AlignRefFile, "-o, --outfile", Options["--outfile"]
1542 )
1543
1544 MiscUtil.ValidateOptionTextValue("--ignoreHydrogens", Options["--ignoreHydrogens"], "yes no")
1545
1546 MiscUtil.ValidateOptionTextValue("--PMLOut", Options["--PMLOut"], "yes no")
1547 MiscUtil.ValidateOptionIntegerValue("--labelFontID", Options["--labelFontID"], {})
1548
1549 MiscUtil.ValidateOptionTextValue("--residueIDs", Options["--residueIDs"], "yes no")
1550
1551 MiscUtil.ValidateOptionTextValue("--residueTypesChain", Options["--residueTypesChain"], "yes no auto")
1552
1553 MiscUtil.ValidateOptionTextValue("--surfaceChain", Options["--surfaceChain"], "yes no auto")
1554 MiscUtil.ValidateOptionTextValue("--surfaceComplex", Options["--surfaceComplex"], "yes no")
1555 MiscUtil.ValidateOptionTextValue("--surfaceChainComplex", Options["--surfaceChainComplex"], "yes no")
1556 MiscUtil.ValidateOptionTextValue(
1557 "--surfaceChainElectrostatics", Options["--surfaceChainElectrostatics"], "yes no auto"
1558 )
1559
1560 MiscUtil.ValidateOptionTextValue(
1561 "--surfaceColorPalette", Options["--surfaceColorPalette"], "RedToWhite WhiteToGreen"
1562 )
1563 MiscUtil.ValidateOptionFloatValue("--surfaceTransparency", Options["--surfaceTransparency"], {">=": 0.0, "<=": 1.0})
1564
1565
1566 # Setup a usage string for docopt...
1567 _docoptUsage_ = """
1568 PyMOLVisualizeSurfaceAndBuriedResidues.py - Visualize surface and buried residues in macromolecules
1569
1570 Usage:
1571 PyMOLVisualizeSurfaceAndBuriedResidues.py [--align <yes or no>] [--alignMethod <align, cealign, super>]
1572 [--alignMode <FirstChain or Complex>] [--alignRefFile <filename>]
1573 [--allowEmptyObjects <yes or no>] [--chainIDs <First, All or ID1,ID2...>]
1574 [--cutoffSASA <number> ] [--labelFontID <number>]
1575 [--ignoreHydrogens <yes or no>] [--ligandIDs <Largest, All or ID1,ID2...> ] [--PMLOut <yes or no>]
1576 [--residueIDs <yes or no> ] [--residueTypes <Type,Color,ResNames,...>] [--residueTypesChain <yes or no>]
1577 [--surfaceChain <yes or no>] [--surfaceChainElectrostatics <yes or no>]
1578 [--surfaceChainComplex <yes or no>] [--surfaceComplex <yes or no>]
1579 [--surfaceAtomTypesColors <ColorType,ColorSpec,...>]
1580 [--surfaceColors <ColorName1,ColorName2>] [--surfaceColorPalette <RedToWhite or WhiteToGreen>]
1581 [--surfaceTransparency <number>] [--overwrite] [-w <dir>] -i <infile1,infile2,infile3...> -o <outfile>
1582 PyMOLVisualizeSurfaceAndBuriedResidues.py -h | --help | -e | --examples
1583
1584 Description:
1585 Generate PyMOL visualization files for viewing surface and buried residues
1586 in macromolecules including proteins and nucleic acids.
1587
1588 The supported input file format are: PDB (.pdb), CIF (.cif)
1589
1590 The supported output file formats are: PyMOL script file (.pml), PyMOL session
1591 file (.pse)
1592
1593 A variety of PyMOL groups and objects may be created for visualization of
1594 surface and buried residues in macromolecules. These groups and objects
1595 correspond to complexes, surfaces, chains, ligands, and inorganics. A complete
1596 hierarchy of all possible PyMOL groups and objects is shown below:
1597
1598 <PDBFileRoot>
1599 .Complex
1600 .Complex
1601 .Surface
1602 .Chain<ID>
1603 .Complex
1604 .Complex
1605 .Surface
1606 .Chain
1607 .Chain
1608 .Surface_Residues
1609 .Chain
1610 .Residues
1611 .Aromatic
1612 .Residues
1613 .Surface
1614 .Hydrophobic
1615 .Residues
1616 .Surface
1617 .Polar
1618 .Residues
1619 .Surface
1620 .Positively_Charged
1621 .Residues
1622 .Surface
1623 .Negatively_Charged
1624 .Residues
1625 .Surface
1626 .Other
1627 .Residues
1628 .Surface
1629 .Surface
1630 .Surface
1631 .Hydrophobicity
1632 .Hydrophobicity_Charge
1633 .Vacuum_Electrostatics
1634 .Contact_Potentials
1635 .Map
1636 .Legend
1637 .Buried_Residues
1638 .Chain
1639 .Residues
1640 .Aromatic
1641 .Residues
1642 .Surface
1643 .Hydrophobic
1644 .Residues
1645 .Surface
1646 .Polar
1647 .Residues
1648 .Surface
1649 .Positively_Charged
1650 .Residues
1651 .Surface
1652 .Negatively_Charged
1653 .Residues
1654 .Surface
1655 .Other
1656 .Residues
1657 .Surface
1658 .Surface
1659 .Surface
1660 .Hydrophobicity
1661 .Hydrophobicity_Charge
1662 .Vacuum_Electrostatics
1663 .Contact_Potentials
1664 .Map
1665 .Legend
1666 .Solvent
1667 .Inorganic
1668 .Ligand<ID>
1669 .Ligand
1670 .Ligand
1671 .BallAndStick
1672 .Ligand<ID>
1673 .Ligand
1674 ... ... ...
1675 .Chain<ID>
1676 ... ... ...
1677 .Ligand<ID>
1678 ... ... ...
1679 .Ligand<ID>
1680 ... ... ...
1681 .Chain<ID>
1682 ... ... ...
1683 <PDBFileRoot>
1684 .Complex
1685 ... ... ...
1686 .Chain<ID>
1687 ... ... ...
1688 .Ligand<ID>
1689 ... ... ...
1690 .Ligand<ID>
1691 ... ... ...
1692 .Chain<ID>
1693 ... ... ...
1694
1695 The hydrophobic and electrostatic surfaces are not created for complete complex
1696 and chain complex in input file(s) by default. A word to the wise: The creation of
1697 surface objects may slow down loading of PML file and generation of PSE file, based
1698 on the size of input complexes. The generation of PSE file may also fail.
1699
1700 Options:
1701 -a, --align <yes or no> [default: no]
1702 Align input files to a reference file before visualization.
1703 --alignMethod <align, cealign, super> [default: super]
1704 Alignment methodology to use for aligning input files to a
1705 reference file.
1706 --alignMode <FirstChain or Complex> [default: FirstChain]
1707 Portion of input and reference files to use for spatial alignment of
1708 input files against reference file. Possible values: FirstChain or
1709 Complex.
1710
1711 The FirstChain mode allows alignment of the first chain in each input
1712 file to the first chain in the reference file along with moving the rest
1713 of the complex to coordinate space of the reference file. The complete
1714 complex in each input file is aligned to the complete complex in reference
1715 file for the Complex mode.
1716 --alignRefFile <filename> [default: FirstInputFile]
1717 Reference input file name. The default is to use the first input file
1718 name specified using '-i, --infiles' option.
1719 --allowEmptyObjects <yes or no> [default: no]
1720 Allow creation of empty PyMOL objects corresponding to solvent and
1721 inorganic atom selections across chains and ligands in input file(s). By
1722 default, the empty objects are marked for deletion.
1723 -c, --chainIDs <First, All or ID1,ID2...> [default: First]
1724 List of chain IDs to use for visualizing surface and buried residues in
1725 macromolecules. Possible values: First, All, or a comma delimited
1726 list of chain IDs. The default is to use the chain ID for the first chain
1727 in each input file.
1728 --cutoffSASA <number> [default: 2.5]
1729 Solvent Accessible Surface Area (SASA) cutoff value in Angstroms**2
1730 for identification of surface and buried residues in chains. The residues
1731 with SASA less than the cutoff value are considered buried residues.
1732 -e, --examples
1733 Print examples.
1734 -h, --help
1735 Print this help message.
1736 -i, --infiles <infile1,infile2,infile3...>
1737 Input file names.
1738 --ignoreHydrogens <yes or no> [default: yes]
1739 Ignore hydrogens for ligand views.
1740 --labelFontID <number> [default: 7]
1741 Font ID for drawing labels. Default: 7 (Sans Bold). Valid values: 5 to 16.
1742 The specified value must be a valid PyMOL font ID. No validation is
1743 performed. The complete lists of valid font IDs is available at:
1744 pymolwiki.org/index.php/Label_font_id. Examples: 5 - Sans;
1745 7 - Sans Bold; 9 - Serif; 10 - Serif Bold.
1746 -l, --ligandIDs <Largest, All or ID1,ID2...> [default: All]
1747 List of ligand IDs to show in chains during visualizing of surface and buried
1748 residues in macromolecules. Possible values: Largest, All, or a comma delimited
1749 list of ligand IDs. The default is to show all ligands present in all or
1750 specified chains in each input file.
1751
1752 Ligands are identified using organic selection operator available in PyMOL.
1753 It'll also identify buffer molecules as ligands. The largest ligand contains
1754 the highest number of heavy atoms.
1755 -o, --outfile <outfile>
1756 Output file name.
1757 -p, --PMLOut <yes or no> [default: yes]
1758 Save PML file during generation of PSE file.
1759 --residueIDs <yes or no> [default: no]
1760 List residue IDs (ResName_ResNum) corresponding to surface and buried
1761 residues. The count and residue distribution for these residues is always
1762 listed.
1763 -r, --residueTypes <Type,Color,ResNames,...> [default: auto]
1764 Residue types, colors, and names to generate for residue groups during
1765 '--residueTypesChain' option. It is only valid for amino acids.
1766
1767 It is a triplet of comma delimited list of amino acid residues type, residues
1768 color, and a space delimited list three letter residue names.
1769
1770 The default values for residue type, color, and name triplets are shown
1771 below:
1772
1773 Aromatic,brightorange,HIS PHE TRP TYR,
1774 Hydrophobic,orange,ALA GLY VAL LEU ILE PRO MET,
1775 Polar,palegreen,ASN GLN SER THR CYS,
1776 Positively_Charged,marine,ARG LYS,
1777 Negatively_Charged,red,ASP GLU
1778
1779 The color name must be a valid PyMOL name. No validation is performed.
1780 An amino acid name may appear across multiple residue types. All other
1781 residues are grouped under 'Other'.
1782 --residueTypesChain <yes or no> [default: auto]
1783 Chain residue types. The residue groups are generated using residue types,
1784 colors, and names specified by '--residueTypes' option. It is only valid for
1785 amino acids. By default, the residue type groups are automatically created
1786 for chains containing amino acids and skipped for chains only containing
1787 nucleic acids.
1788 --surfaceChain <yes or no> [default: auto]
1789 Surfaces around individual chain colored by hydrophobicity alone and
1790 both hydrophobicity and charge. The hydrophobicity surface is colored
1791 at residue level using Eisenberg hydrophobicity scale for residues and color
1792 gradient specified by '--surfaceColorPalette' option. The hydrophobicity and
1793 charge surface is colored [ Ref 140 ] at atom level using colors specified for
1794 groups of atoms by '--surfaceAtomTypesColors' option. This scheme allows
1795 simultaneous mapping of hyrophobicity and charge values on the surfaces.
1796
1797 This option is only valid for amino acids. By default, both surfaces are
1798 automatically created for chains containing amino acids and skipped for
1799 chains containing only nucleic acids.
1800
1801 In addition, generic surfaces colored by '--surfaceColor' are always created
1802 for chain residues containing amino acids and nucleic acids.
1803 --surfaceChainElectrostatics <yes or no> [default: no]
1804 Vacuum electrostatics contact potential surface around individual
1805 chain. A word to the wise from PyMOL documentation: The computed protein
1806 contact potentials are only qualitatively useful, due to short cutoffs,
1807 truncation, and lack of solvent "screening".
1808
1809 This option is only valid for amino acids. By default, the electrostatics surface
1810 is automatically created for chains containing amino acids and
1811 skipped for chains containing only nucleic acids.
1812 --surfaceChainComplex <yes or no> [default: no]
1813 Hydrophobic surface around chain complex. The surface is colored by
1814 hydrophobicity. It is only valid for amino acids.
1815 --surfaceComplex <yes or no> [default: no]
1816 Hydrophobic surface around complete complex. The surface is colored by
1817 hydrophobicity. It is only valid for amino acids.
1818 --surfaceAtomTypesColors <ColorType,ColorSpec,...> [default: auto]
1819 Atom colors for generating surfaces colored by hyrophobicity and charge
1820 around chains and pockets in proteins. It's a pairwise comma delimited list
1821 of atom color type and color specification for goups of atoms.
1822
1823 The default values for color types [ Ref 140 ] along wth color specifications
1824 are shown below:
1825
1826 HydrophobicAtomsColor, yellow,
1827 NegativelyChargedAtomsColor, red,
1828 PositivelyChargedAtomsColor, blue,
1829 OtherAtomsColor, gray90
1830
1831 The color names must be valid PyMOL names.
1832
1833 The color values may also be specified as space delimited RGB triplets:
1834
1835 HydrophobicAtomsColor, 0.95 0.78 0.0,
1836 NegativelyChargedAtomsColor, 1.0 0.4 0.4,
1837 PositivelyChargedAtomsColor, 0.2 0.5 0.8,
1838 OtherAtomsColor, 0.95 0.95 0.95
1839
1840 --surfaceColors <ColorName1,ColorName2> [default: lightblue,salmon]
1841 Color names for surface and burieds residues in chains. These colors are not
1842 used for surfaces colored by hydrophobicity and charge. The color names
1843 must be valid PyMOL names.
1844 --surfaceColorPalette <RedToWhite or WhiteToGreen> [default: RedToWhite]
1845 Color palette for hydrophobic surfaces around chains and pockets in proteins.
1846 Possible values: RedToWhite or WhiteToGreen from most hydrophobic amino
1847 acid to least hydrophobic. The colors values for amino acids are taken from
1848 color_h script available as part of the Script Library at PyMOL Wiki.
1849 --surfaceTransparency <number> [default: 0.25]
1850 Surface transparency for molecular surfaces.
1851 --overwrite
1852 Overwrite existing files.
1853 -w, --workingdir <dir>
1854 Location of working directory which defaults to the current directory.
1855
1856 Examples:
1857 To visualize surface and buried residues in the first chain along with the
1858 largest ligand in the first chain, solvents, and inorganics, in a PDB file, and
1859 generate a PML file, type:
1860
1861 % PyMOLVisualizeSurfaceAndBuriedResidues.py -i Sample4.pdb
1862 -o Sample4.pml
1863
1864 To visualize surface and buries residues in all chain along with all ligands,
1865 solvents, and inorganics, in a PDB file, and generate a PML file, type:
1866
1867 % PyMOLVisualizeSurfaceAndBuriedResidues.py -c All -l All
1868 -i Sample4.pdb -o Sample4.pml
1869
1870 To visualize surface and buried residues in the first chain at a specific
1871 cutoff using specifc colors for surfaces corresponding to surface and
1872 buried residues, and generate a PML file, type:
1873
1874 % PyMOLVisualizeSurfaceAndBuriedResidues.py --cutoffSASA 3
1875 --surfaceColors "blue,red" -i Sample4.pdb -o Sample4.pml
1876
1877 To visualize surface and buried residues in the first chain along with the
1878 largest ligand in the first chain, solvents, and inorganics, in PDB files, along
1879 with aligning first chain in each input file to the first chain inand generate a
1880 PML file, type:
1881
1882 % PyMOLVisualizeSurfaceAndBuriedResidues.py --align yes
1883 -i "Sample5.pdb,Sample6.pdb,Sample7.pdb"
1884 -o SampleOut.pml
1885
1886 Author:
1887 Manish Sud(msud@san.rr.com)
1888
1889 See also:
1890 DownloadPDBFiles.pl, PyMOLVisualizeCavities.py,
1891 PyMOLVisualizeCryoEMDensity.py, PyMOLVisualizeElectronDensity.py,
1892 PyMOLVisualizeInterfaces.py, PyMOLVisualizeMacromolecules.py
1893
1894 Copyright:
1895 Copyright (C) 2026 Manish Sud. All rights reserved.
1896
1897 The functionality available in this script is implemented using PyMOL, a
1898 molecular visualization system on an open source foundation originally
1899 developed by Warren DeLano.
1900
1901 This file is part of MayaChemTools.
1902
1903 MayaChemTools is free software; you can redistribute it and/or modify it under
1904 the terms of the GNU Lesser General Public License as published by the Free
1905 Software Foundation; either version 3 of the License, or (at your option) any
1906 later version.
1907
1908 """
1909
1910 if __name__ == "__main__":
1911 main()