MayaChemTools

    1 #!/bin/env python
    2 #
    3 # File: PyMOLVisualizeCavities.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     GenerateCavitiesVisualization()
   84 
   85     MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName)
   86     MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime))
   87 
   88 
   89 def GenerateCavitiesVisualization():
   90     """Generate visualization for cavities."""
   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     PMLCmds.append("""cmd.set("cavity_cull", %.1f, "", 0)""" % (OptionsInfo["CavityCullSize"]))
  198     PMLCmds.append("""cmd.set("surface_cavity_radius", -%.1f, "", 0)""" % (OptionsInfo["CavityRadius"]))
  199     PMLCmds.append("""cmd.set("surface_cavity_cutoff", -%.1f, "", 0)""" % (OptionsInfo["CavityCutoff"]))
  200 
  201     PML = "\n".join(PMLCmds)
  202 
  203     OutFH.write("""\n""\n"Setting up PyMOL gobal parameters..."\n""\n""")
  204     OutFH.write("%s\n" % PML)
  205 
  206 
  207 def WriteAlignReference(OutFH):
  208     """Setup object for alignment reference."""
  209 
  210     RefFileInfo = OptionsInfo["RefFileInfo"]
  211     RefFile = RefFileInfo["RefFileName"]
  212     RefName = RefFileInfo["PyMOLObjectName"]
  213 
  214     PMLCmds = []
  215     PMLCmds.append("""cmd.load("%s", "%s")""" % (RefFile, RefName))
  216     PMLCmds.append("""cmd.hide("everything", "%s")""" % (RefName))
  217     PMLCmds.append("""cmd.disable("%s")""" % (RefName))
  218     PML = "\n".join(PMLCmds)
  219 
  220     OutFH.write("""\n""\n"Loading %s and setting up view for align reference..."\n""\n""" % RefFile)
  221     OutFH.write("%s\n" % PML)
  222 
  223 
  224 def WriteAlignComplex(OutFH, FileIndex, PyMOLObjectNames):
  225     """Setup alignment of complex to reference."""
  226 
  227     RefFileInfo = OptionsInfo["RefFileInfo"]
  228     RefName = RefFileInfo["PyMOLObjectName"]
  229 
  230     ComplexName = PyMOLObjectNames["Complex"]
  231 
  232     if re.match("^FirstChain$", OptionsInfo["AlignMode"], re.I):
  233         RefFirstChainID = RefFileInfo["ChainsAndLigandsInfo"]["ChainIDs"][0]
  234         RefAlignSelection = "%s and chain %s" % (RefName, RefFirstChainID)
  235 
  236         ComplexFirstChainID = RetrieveFirstChainID(FileIndex)
  237         ComplexAlignSelection = "%s and chain %s" % (ComplexName, ComplexFirstChainID)
  238     else:
  239         RefAlignSelection = RefName
  240         ComplexAlignSelection = ComplexName
  241 
  242     PML = PyMOLUtil.SetupPMLForAlignment(OptionsInfo["AlignMethod"], RefAlignSelection, ComplexAlignSelection)
  243     OutFH.write("""\n""\n"Aligning %s against reference %s ..."\n""\n""" % (ComplexAlignSelection, RefAlignSelection))
  244     OutFH.write("%s\n" % PML)
  245 
  246 
  247 def DeleteAlignReference(OutFH):
  248     """Delete alignment reference object."""
  249 
  250     RefName = OptionsInfo["RefFileInfo"]["PyMOLObjectName"]
  251     OutFH.write("""\n""\n"Deleting alignment reference object %s..."\n""\n""" % RefName)
  252     OutFH.write("""cmd.delete("%s")\n""" % RefName)
  253 
  254 
  255 def WriteComplexView(OutFH, FileIndex, PyMOLObjectNames, FirstComplex):
  256     """Write out PML for viewing polymer complex."""
  257 
  258     # Setup complex...
  259     Infile = OptionsInfo["InfilesInfo"]["InfilesNames"][FileIndex]
  260     PML = PyMOLUtil.SetupPMLForPolymerComplexView(PyMOLObjectNames["Complex"], Infile, True)
  261     OutFH.write("""\n""\n"Loading %s and setting up view for complex..."\n""\n""" % Infile)
  262     OutFH.write("%s\n" % PML)
  263 
  264     if OptionsInfo["Align"]:
  265         # No need to align complex on to itself...
  266         if not (re.match("^FirstInputFile$", OptionsInfo["AlignRefFile"], re.I) and FirstComplex):
  267             WriteAlignComplex(OutFH, FileIndex, PyMOLObjectNames)
  268 
  269     if OptionsInfo["SurfaceComplex"]:
  270         # Setup hydrophobic surface...
  271         PML = PyMOLUtil.SetupPMLForHydrophobicSurfaceView(
  272             PyMOLObjectNames["ComplexHydrophobicSurface"],
  273             PyMOLObjectNames["Complex"],
  274             ColorPalette=OptionsInfo["SurfaceColorPalette"],
  275             Enable=False,
  276         )
  277         OutFH.write("\n%s\n" % PML)
  278 
  279     # Setup complex group...
  280     GenerateAndWritePMLForGroup(
  281         OutFH, PyMOLObjectNames["ComplexGroup"], PyMOLObjectNames["ComplexGroupMembers"], False, "close"
  282     )
  283 
  284 
  285 def WriteChainView(OutFH, FileIndex, PyMOLObjectNames, ChainID):
  286     """Write out PML for viewing chain."""
  287 
  288     OutFH.write("""\n""\n"Setting up views for chain %s..."\n""\n""" % ChainID)
  289 
  290     ChainComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
  291 
  292     # Setup chain complex group view...
  293     WriteChainComplexViews(OutFH, FileIndex, PyMOLObjectNames, ChainID)
  294 
  295     # Setup chain view...
  296     WriteChainAloneViews(OutFH, FileIndex, PyMOLObjectNames, ChainID)
  297 
  298     # Setup chain solvent view...
  299     PML = PyMOLUtil.SetupPMLForSolventView(PyMOLObjectNames["Chains"][ChainID]["Solvent"], ChainComplexName, False)
  300     OutFH.write("\n%s\n" % PML)
  301 
  302     # Setup chain inorganic view...
  303     PML = PyMOLUtil.SetupPMLForInorganicView(PyMOLObjectNames["Chains"][ChainID]["Inorganic"], ChainComplexName, False)
  304     OutFH.write("\n%s\n" % PML)
  305 
  306 
  307 def WriteChainComplexViews(OutFH, FileIndex, PyMOLObjectNames, ChainID):
  308     """Write chain complex views."""
  309 
  310     # Setup chain complex...
  311     ChainComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
  312     PML = PyMOLUtil.SetupPMLForPolymerChainComplexView(ChainComplexName, PyMOLObjectNames["Complex"], ChainID, True)
  313     OutFH.write("%s\n" % PML)
  314 
  315     if OptionsInfo["SurfaceChainComplex"]:
  316         # Setup hydrophobic surface...
  317         PML = PyMOLUtil.SetupPMLForHydrophobicSurfaceView(
  318             PyMOLObjectNames["Chains"][ChainID]["ChainComplexHydrophobicSurface"],
  319             ChainComplexName,
  320             ColorPalette=OptionsInfo["SurfaceColorPalette"],
  321             Enable=False,
  322         )
  323         OutFH.write("\n%s\n" % PML)
  324 
  325     # Setup chain complex group...
  326     GenerateAndWritePMLForGroup(
  327         OutFH,
  328         PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroup"],
  329         PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"],
  330         False,
  331         "close",
  332     )
  333 
  334 
  335 def WriteChainAloneViews(OutFH, FileIndex, PyMOLObjectNames, ChainID):
  336     """Write individual chain views."""
  337 
  338     ChainComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
  339 
  340     # Setup chain view...
  341     ChainName = PyMOLObjectNames["Chains"][ChainID]["ChainAlone"]
  342     PML = PyMOLUtil.SetupPMLForPolymerChainView(ChainName, ChainComplexName, True)
  343     OutFH.write("\n%s\n" % PML)
  344 
  345     WriteChainAloneResidueTypesView(OutFH, FileIndex, PyMOLObjectNames, ChainID)
  346     WriteChainAloneCavitiesAndSurfacesView(OutFH, FileIndex, PyMOLObjectNames, ChainID)
  347 
  348     # Setup chain group...
  349     GenerateAndWritePMLForGroup(
  350         OutFH,
  351         PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroup"],
  352         PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"],
  353         True,
  354         "open",
  355     )
  356 
  357 
  358 def WriteChainLigandView(OutFH, FileIndex, PyMOLObjectNames, ChainID, LigandID):
  359     """Write out PML for viewing ligand in a chain."""
  360 
  361     GroupID = "Ligand"
  362     ComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
  363 
  364     # Setup main object...
  365     GroupTypeObjectID = "%s" % (GroupID)
  366     GroupTypeObjectName = PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupTypeObjectID]
  367 
  368     OutFH.write("""\n""\n"Setting up views for ligand %s in chain %s..."\n""\n""" % (LigandID, ChainID))
  369     PML = PyMOLUtil.SetupPMLForLigandView(
  370         GroupTypeObjectName, ComplexName, LigandID, Enable=True, IgnoreHydrogens=OptionsInfo["IgnoreHydrogens"]
  371     )
  372     OutFH.write("%s\n" % PML)
  373 
  374     # Setup ball and stick view...
  375     BallAndStickNameID = "%sBallAndStick" % (GroupID)
  376     BallAndStickName = PyMOLObjectNames["Ligands"][ChainID][LigandID][BallAndStickNameID]
  377     PML = PyMOLUtil.SetupPMLForBallAndStickView(BallAndStickName, GroupTypeObjectName, Enable=False)
  378     OutFH.write("\n%s\n" % PML)
  379 
  380     # Setup group....
  381     GroupNameID = "%sGroup" % (GroupID)
  382     GroupMembersID = "%sGroupMembers" % (GroupID)
  383     GroupName = PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupNameID]
  384     GroupMembers = PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID]
  385 
  386     Action = "close"
  387     Enable = True
  388     GenerateAndWritePMLForGroup(OutFH, GroupName, GroupMembers, Enable, Action)
  389 
  390 
  391 def WriteChainAloneCavitiesAndSurfacesView(OutFH, FileIndex, PyMOLObjectNames, ChainID):
  392     """Write out PML for viewing cavities and pockets in chains."""
  393 
  394     if not GetChainAloneContainsSurfacesStatus(FileIndex, ChainID):
  395         return
  396 
  397     ChainName = PyMOLObjectNames["Chains"][ChainID]["ChainAlone"]
  398 
  399     for SubGroupType in ["Cavities", "Surface"]:
  400         SubGroupID = "ChainAlone%sGroup" % (SubGroupType)
  401         SubGroupMembersID = "%sMembers" % (SubGroupID)
  402 
  403         ProcessingCavities = True if re.match("^Cavities$", SubGroupType, re.I) else False
  404 
  405         # Turn off lines display for cavity surfaces...
  406         DisplayStyle = None if ProcessingCavities else "cartoon"
  407 
  408         # Setup a generic color surface...
  409         SurfaceID = "%sSurface" % (SubGroupID)
  410         SurfaceName = PyMOLObjectNames["Chains"][ChainID][SurfaceID]
  411         ColorName = OptionsInfo["SurfaceCavityColor"] if ProcessingCavities else OptionsInfo["SurfaceColor"]
  412         EnableStatus = True if ProcessingCavities else False
  413         PML = PyMOLUtil.SetupPMLForSurfaceView(SurfaceName, ChainName, Enable=EnableStatus, Color=ColorName)
  414         OutFH.write("\n%s\n" % PML)
  415 
  416         if ProcessingCavities:
  417             OutFH.write(
  418                 """cmd.set("surface_cavity_mode", %d, "%s")\n""" % (OptionsInfo["SurfaceCavityMode"], SurfaceName)
  419             )
  420 
  421         if GetChainAloneSurfaceChainStatus(FileIndex, ChainID):
  422             # Setup surface colored by hydrophobicity...
  423             HydrophobicSurfaceID = "%sHydrophobicSurface" % (SubGroupID)
  424             HydrophobicSurfaceName = PyMOLObjectNames["Chains"][ChainID][HydrophobicSurfaceID]
  425             PML = PyMOLUtil.SetupPMLForHydrophobicSurfaceView(
  426                 HydrophobicSurfaceName, ChainName, ColorPalette=OptionsInfo["SurfaceColorPalette"], Enable=False
  427             )
  428             OutFH.write("\n%s\n" % PML)
  429 
  430             if ProcessingCavities:
  431                 OutFH.write(
  432                     """cmd.set("surface_cavity_mode", %d, "%s")\n"""
  433                     % (OptionsInfo["SurfaceCavityMode"], HydrophobicSurfaceName)
  434                 )
  435 
  436             # Setup surface colored by hyrdophobicity and charge...
  437             HydrophobicChargeSurfaceID = "%sHydrophobicChargeSurface" % (SubGroupID)
  438             HydrophobicChargeSurfaceName = PyMOLObjectNames["Chains"][ChainID][HydrophobicChargeSurfaceID]
  439             PML = PyMOLUtil.SetupPMLForHydrophobicAndChargeSurfaceView(
  440                 HydrophobicChargeSurfaceName,
  441                 ChainName,
  442                 OptionsInfo["AtomTypesColorNames"]["HydrophobicAtomsColor"],
  443                 OptionsInfo["AtomTypesColorNames"]["NegativelyChargedAtomsColor"],
  444                 OptionsInfo["AtomTypesColorNames"]["PositivelyChargedAtomsColor"],
  445                 OptionsInfo["AtomTypesColorNames"]["OtherAtomsColor"],
  446                 Enable=False,
  447                 DisplayAs=None,
  448             )
  449             OutFH.write("\n%s\n" % PML)
  450 
  451             if ProcessingCavities:
  452                 OutFH.write(
  453                     """cmd.set("surface_cavity_mode", %d, "%s")\n"""
  454                     % (OptionsInfo["SurfaceCavityMode"], HydrophobicChargeSurfaceName)
  455                 )
  456 
  457         if GetChainAloneSurfaceChainElectrostaticsStatus(FileIndex, ChainID):
  458             # Setup electrostatics surface...
  459             ElectrostaticsGroupID = "%sElectrostaticsGroup" % (SubGroupID)
  460             ElectrostaticsGroupMembersID = "%sElectrostaticsGroupMembers" % (SubGroupID)
  461             ElectrostaticsGroupName = PyMOLObjectNames["Chains"][ChainID][ElectrostaticsGroupID]
  462             ElectrostaticsGroupMembers = PyMOLObjectNames["Chains"][ChainID][ElectrostaticsGroupMembersID]
  463             WriteSurfaceElectrostaticsView(
  464                 SubGroupType,
  465                 OutFH,
  466                 ChainName,
  467                 ElectrostaticsGroupName,
  468                 ElectrostaticsGroupMembers,
  469                 DisplayAs=DisplayStyle,
  470                 SurfaceCavityMode=OptionsInfo["SurfaceCavityMode"],
  471             )
  472 
  473         # Setup surface group...
  474         GenerateAndWritePMLForGroup(
  475             OutFH,
  476             PyMOLObjectNames["Chains"][ChainID][SubGroupID],
  477             PyMOLObjectNames["Chains"][ChainID][SubGroupMembersID],
  478             True,
  479             "open",
  480         )
  481 
  482 
  483 def WriteChainAloneResidueTypesView(OutFH, FileIndex, PyMOLObjectNames, ChainID):
  484     """Write out PML for viewing residue types for a chain."""
  485 
  486     if not GetChainAloneResidueTypesStatus(FileIndex, ChainID):
  487         return
  488 
  489     ChainName = PyMOLObjectNames["Chains"][ChainID]["ChainAlone"]
  490 
  491     # Setup residue types objects...
  492     ResiduesGroupIDPrefix = "ChainAloneResidues"
  493     for SubGroupType in ["Aromatic", "Hydrophobic", "Polar", "Positively_Charged", "Negatively_Charged", "Other"]:
  494         SubGroupID = re.sub("_", "", SubGroupType)
  495 
  496         ResiduesObjectID = "%s%sResidues" % (ResiduesGroupIDPrefix, SubGroupID)
  497         ResiduesObjectName = PyMOLObjectNames["Chains"][ChainID][ResiduesObjectID]
  498 
  499         ResiduesSurfaceObjectID = "%s%sSurface" % (ResiduesGroupIDPrefix, SubGroupID)
  500         ResiduesSurfaceObjectName = PyMOLObjectNames["Chains"][ChainID][ResiduesSurfaceObjectID]
  501 
  502         ResiduesColor = OptionsInfo["ResidueTypesParams"][SubGroupType]["Color"]
  503         ResiduesNames = OptionsInfo["ResidueTypesParams"][SubGroupType]["Residues"]
  504 
  505         NegateResidueNames = True if re.match("^Other$", SubGroupType, re.I) else False
  506         WriteResidueTypesResiduesAndSurfaceView(
  507             OutFH,
  508             ChainName,
  509             ResiduesObjectName,
  510             ResiduesSurfaceObjectName,
  511             ResiduesColor,
  512             ResiduesNames,
  513             NegateResidueNames,
  514         )
  515 
  516         # Setup sub groups for residue types..
  517         ResiduesSubGroupID = "%s%sGroup" % (ResiduesGroupIDPrefix, SubGroupID)
  518         ResiduesSubGroupMembersID = "%s%sGroupMembers" % (ResiduesGroupIDPrefix, SubGroupID)
  519 
  520         # Setup residue type sub groups...
  521         GenerateAndWritePMLForGroup(
  522             OutFH,
  523             PyMOLObjectNames["Chains"][ChainID][ResiduesSubGroupID],
  524             PyMOLObjectNames["Chains"][ChainID][ResiduesSubGroupMembersID],
  525             True,
  526             "close",
  527         )
  528 
  529     # Setup residue types group...
  530     GenerateAndWritePMLForGroup(
  531         OutFH,
  532         PyMOLObjectNames["Chains"][ChainID]["ChainAloneResiduesGroup"],
  533         PyMOLObjectNames["Chains"][ChainID]["ChainAloneResiduesGroupMembers"],
  534         False,
  535         "close",
  536     )
  537 
  538 
  539 def WriteResidueTypesResiduesAndSurfaceView(
  540     OutFH, SelectionObjectName, Name, SurfaceName, ResiduesColor, ResiduesNames, NegateResidueNames
  541 ):
  542     """Write residue types residues and surface view."""
  543 
  544     ResidueNamesSelection = "+".join(ResiduesNames)
  545     if NegateResidueNames:
  546         Selection = "%s and (not resn %s)" % (SelectionObjectName, ResidueNamesSelection)
  547     else:
  548         Selection = "%s and (resn %s)" % (SelectionObjectName, ResidueNamesSelection)
  549 
  550     # Setup residues...
  551     PML = PyMOLUtil.SetupPMLForSelectionDisplayView(Name, Selection, "lines", ResiduesColor, True)
  552     OutFH.write("\n%s\n" % PML)
  553 
  554     # Setup surface...
  555     PML = PyMOLUtil.SetupPMLForSelectionDisplayView(SurfaceName, Selection, "surface", ResiduesColor, True)
  556     OutFH.write("\n%s\n" % PML)
  557 
  558 
  559 def WriteSurfaceElectrostaticsView(
  560     Mode,
  561     OutFH,
  562     SelectionObjectName,
  563     ElectrostaticsGroupName,
  564     ElectrostaticsGroupMembers,
  565     DisplayAs=None,
  566     SurfaceCavityMode=2,
  567 ):
  568     """Write out PML for viewing surface electrostatics."""
  569 
  570     if len(ElectrostaticsGroupMembers) == 5:
  571         Name, ContactPotentialName, MapName, LegendName, VolumeName = ElectrostaticsGroupMembers
  572     else:
  573         Name, ContactPotentialName, MapName, LegendName = ElectrostaticsGroupMembers
  574         VolumeName = None
  575 
  576     PMLCmds = []
  577 
  578     # Setup chain...
  579     PMLCmds.append("""cmd.create("%s", "(%s)")""" % (Name, SelectionObjectName))
  580 
  581     # Setup vacuum electrostatics surface along with associated objects...
  582     PMLCmds.append("""util.protein_vacuum_esp("%s", mode=2, quiet=0, _self=cmd)""" % (Name))
  583     PMLCmds.append("""cmd.set_name("%s_e_chg", "%s")""" % (Name, ContactPotentialName))
  584 
  585     if DisplayAs is not None:
  586         PMLCmds.append("""cmd.show("%s", "(%s)")""" % (DisplayAs, ContactPotentialName))
  587 
  588     if re.match("^Cavities$", Mode, re.I):
  589         if SurfaceCavityMode is not None:
  590             PMLCmds.append("""cmd.set("surface_cavity_mode", %d, "%s")\n""" % (SurfaceCavityMode, ContactPotentialName))
  591 
  592     PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(ContactPotentialName, Enable=True))
  593 
  594     PMLCmds.append("""cmd.set_name("%s_e_map", "%s")""" % (Name, MapName))
  595     PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(MapName, Enable=False))
  596 
  597     PMLCmds.append("""cmd.set_name("%s_e_pot", "%s")""" % (Name, LegendName))
  598     PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(LegendName, Enable=False))
  599 
  600     if VolumeName is not None:
  601         PMLCmds.append("""cmd.volume("%s", "%s", "%s", "(%s)")""" % (VolumeName, MapName, "esp", Name))
  602         PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(VolumeName, Enable=False))
  603 
  604     # Delete name and take it out from the group membership. It is
  605     # is already part of ContactPotential object.
  606     PMLCmds.append("""cmd.delete("%s")""" % (Name))
  607     ElectrostaticsGroupMembers.pop(0)
  608 
  609     PML = "\n".join(PMLCmds)
  610 
  611     OutFH.write("\n%s\n" % PML)
  612 
  613     # Setup group...
  614     GenerateAndWritePMLForGroup(OutFH, ElectrostaticsGroupName, ElectrostaticsGroupMembers, False, "close")
  615 
  616 
  617 def GenerateAndWritePMLForGroup(OutFH, GroupName, GroupMembers, Enable=False, Action="close"):
  618     """Generate and write PML for group."""
  619 
  620     PML = PyMOLUtil.SetupPMLForGroup(GroupName, GroupMembers, Enable, Action)
  621     OutFH.write("""\n""\n"Setting up group %s..."\n""\n""" % GroupName)
  622     OutFH.write("%s\n" % PML)
  623 
  624 
  625 def GeneratePyMOLSessionFile():
  626     """Generate PME file from PML file."""
  627 
  628     PSEOutfile = OptionsInfo["PSEOutfile"]
  629     PMLOutfile = OptionsInfo["PMLOutfile"]
  630 
  631     MiscUtil.PrintInfo("\nGenerating file %s..." % PSEOutfile)
  632 
  633     PyMOLUtil.ConvertPMLFileToPSEFile(PMLOutfile, PSEOutfile)
  634 
  635     if not os.path.exists(PSEOutfile):
  636         MiscUtil.PrintWarning("Failed to generate PSE file, %s..." % (PSEOutfile))
  637 
  638     if not OptionsInfo["PMLOut"]:
  639         MiscUtil.PrintInfo("Deleting file %s..." % PMLOutfile)
  640         os.remove(PMLOutfile)
  641 
  642 
  643 def DeleteEmptyPyMOLObjects(OutFH, FileIndex, PyMOLObjectNames):
  644     """Delete empty PyMOL objects."""
  645 
  646     if OptionsInfo["AllowEmptyObjects"]:
  647         return
  648 
  649     SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
  650     for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
  651         OutFH.write("""\n""\n"Checking and deleting empty objects for chain %s..."\n""\n""" % (ChainID))
  652 
  653         # Delete any chain level objects...
  654         WritePMLToCheckAndDeleteEmptyObjects(OutFH, PyMOLObjectNames["Chains"][ChainID]["Solvent"])
  655         WritePMLToCheckAndDeleteEmptyObjects(OutFH, PyMOLObjectNames["Chains"][ChainID]["Inorganic"])
  656 
  657         # Delete residue type objects...
  658         DeleteEmptyChainResidueTypesObjects(OutFH, FileIndex, PyMOLObjectNames, ChainID)
  659 
  660 
  661 def DeleteEmptyChainResidueTypesObjects(OutFH, FileIndex, PyMOLObjectNames, ChainID):
  662     """Delete empty chain residue objects."""
  663 
  664     if not GetChainAloneResidueTypesStatus(FileIndex, ChainID):
  665         return
  666 
  667     ResiduesGroupIDPrefix = "ChainAloneResidues"
  668     for GroupType in ["Aromatic", "Hydrophobic", "Polar", "Positively_Charged", "Negatively_Charged", "Other"]:
  669         GroupID = re.sub("_", "", GroupType)
  670 
  671         ResiduesGroupID = "%s%sGroup" % (ResiduesGroupIDPrefix, GroupID)
  672         GroupName = PyMOLObjectNames["Chains"][ChainID][ResiduesGroupID]
  673 
  674         GroupObjectNamesList = []
  675 
  676         ResiduesObjectID = "%s%sResidues" % (ResiduesGroupIDPrefix, GroupID)
  677         ResiduesObjectName = PyMOLObjectNames["Chains"][ChainID][ResiduesObjectID]
  678         GroupObjectNamesList.append(ResiduesObjectName)
  679 
  680         ResiduesSurfaceObjectID = "%s%sSurface" % (ResiduesGroupIDPrefix, GroupID)
  681         ResiduesSurfaceObjectName = PyMOLObjectNames["Chains"][ChainID][ResiduesSurfaceObjectID]
  682         GroupObjectNamesList.append(ResiduesSurfaceObjectName)
  683 
  684         GroupObjectNames = ",".join(GroupObjectNamesList)
  685         WritePMLToCheckAndDeleteEmptyObjects(OutFH, GroupObjectNames, GroupName)
  686 
  687 
  688 def WritePMLToCheckAndDeleteEmptyObjects(OutFH, ObjectName, ParentObjectName=None):
  689     """Write PML to check and delete empty PyMOL objects."""
  690 
  691     if ParentObjectName is None:
  692         PML = """CheckAndDeleteEmptyObjects("%s")""" % (ObjectName)
  693     else:
  694         PML = """CheckAndDeleteEmptyObjects("%s", "%s")""" % (ObjectName, ParentObjectName)
  695 
  696     OutFH.write("%s\n" % PML)
  697 
  698 
  699 def SetupPyMOLObjectNames(FileIndex):
  700     """Setup hierarchy of PyMOL groups and objects for ligand centric views of
  701     chains and ligands present in input file.
  702     """
  703 
  704     PyMOLObjectNames = {}
  705     PyMOLObjectNames["Chains"] = {}
  706     PyMOLObjectNames["Ligands"] = {}
  707 
  708     # Setup groups and objects for complex...
  709     SetupPyMOLObjectNamesForComplex(FileIndex, PyMOLObjectNames)
  710 
  711     # Setup groups and objects for chain...
  712     SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
  713     for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
  714         SetupPyMOLObjectNamesForChain(FileIndex, PyMOLObjectNames, ChainID)
  715 
  716         # Setup groups and objects for ligand...
  717         for LigandID in SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID]:
  718             SetupPyMOLObjectNamesForLigand(FileIndex, PyMOLObjectNames, ChainID, LigandID)
  719 
  720     return PyMOLObjectNames
  721 
  722 
  723 def SetupPyMOLObjectNamesForComplex(FileIndex, PyMOLObjectNames):
  724     """Setup groups and objects for complex."""
  725 
  726     PDBFileRoot = OptionsInfo["InfilesInfo"]["InfilesRoots"][FileIndex]
  727 
  728     PDBGroupName = "%s" % PDBFileRoot
  729     PyMOLObjectNames["PDBGroup"] = PDBGroupName
  730     PyMOLObjectNames["PDBGroupMembers"] = []
  731 
  732     ComplexGroupName = "%s.Complex" % PyMOLObjectNames["PDBGroup"]
  733     PyMOLObjectNames["ComplexGroup"] = ComplexGroupName
  734     PyMOLObjectNames["PDBGroupMembers"].append(ComplexGroupName)
  735 
  736     PyMOLObjectNames["Complex"] = "%s.Complex" % ComplexGroupName
  737     if OptionsInfo["SurfaceComplex"]:
  738         PyMOLObjectNames["ComplexHydrophobicSurface"] = "%s.Surface" % ComplexGroupName
  739 
  740     PyMOLObjectNames["ComplexGroupMembers"] = []
  741     PyMOLObjectNames["ComplexGroupMembers"].append(PyMOLObjectNames["Complex"])
  742     if OptionsInfo["SurfaceComplex"]:
  743         PyMOLObjectNames["ComplexGroupMembers"].append(PyMOLObjectNames["ComplexHydrophobicSurface"])
  744 
  745 
  746 def SetupPyMOLObjectNamesForChain(FileIndex, PyMOLObjectNames, ChainID):
  747     """Setup groups and objects for chain."""
  748 
  749     PDBGroupName = PyMOLObjectNames["PDBGroup"]
  750 
  751     PyMOLObjectNames["Chains"][ChainID] = {}
  752     PyMOLObjectNames["Ligands"][ChainID] = {}
  753 
  754     # Set up chain group and chain objects...
  755     ChainGroupName = "%s.Chain%s" % (PDBGroupName, ChainID)
  756     PyMOLObjectNames["Chains"][ChainID]["ChainGroup"] = ChainGroupName
  757     PyMOLObjectNames["PDBGroupMembers"].append(ChainGroupName)
  758     PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"] = []
  759 
  760     # Setup chain complex group and objects...
  761     ChainComplexGroupName = "%s.Complex" % (ChainGroupName)
  762     PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroup"] = ChainComplexGroupName
  763     PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(ChainComplexGroupName)
  764 
  765     PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"] = []
  766 
  767     Name = "%s.Complex" % (ChainComplexGroupName)
  768     PyMOLObjectNames["Chains"][ChainID]["ChainComplex"] = Name
  769     PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"].append(Name)
  770 
  771     if OptionsInfo["SurfaceChainComplex"]:
  772         Name = "%s.Surface" % (ChainComplexGroupName)
  773         PyMOLObjectNames["Chains"][ChainID]["ChainComplexHydrophobicSurface"] = Name
  774         PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"].append(Name)
  775 
  776     # Setup up a group for individual chains...
  777     ChainAloneGroupName = "%s.Chain" % (ChainGroupName)
  778     PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroup"] = ChainAloneGroupName
  779     PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(ChainAloneGroupName)
  780 
  781     PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"] = []
  782 
  783     Name = "%s.Chain" % (ChainAloneGroupName)
  784     PyMOLObjectNames["Chains"][ChainID]["ChainAlone"] = Name
  785     PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"].append(Name)
  786 
  787     if GetChainAloneResidueTypesStatus(FileIndex, ChainID):
  788         # Setup residue type group and its subgroups...
  789         ResiduesGroupName = "%s.Residues" % (ChainAloneGroupName)
  790 
  791         ResiduesGroupIDPrefix = "ChainAloneResidues"
  792         ResiduesGroupID = "%sGroup" % ResiduesGroupIDPrefix
  793 
  794         # Add residue group to chain alone group...
  795         PyMOLObjectNames["Chains"][ChainID][ResiduesGroupID] = ResiduesGroupName
  796         PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"].append(ResiduesGroupName)
  797 
  798         # Initialize residue group members...
  799         ResiduesGroupMembersID = "%sGroupMembers" % ResiduesGroupIDPrefix
  800         PyMOLObjectNames["Chains"][ChainID][ResiduesGroupMembersID] = []
  801 
  802         # Setup residues sub groups and its members...
  803         for SubGroupType in ["Aromatic", "Hydrophobic", "Polar", "Positively_Charged", "Negatively_Charged", "Other"]:
  804             SubGroupID = re.sub("_", "", SubGroupType)
  805 
  806             ResiduesSubGroupName = "%s.%s" % (ResiduesGroupName, SubGroupType)
  807             ResiduesSubGroupID = "%s%sGroup" % (ResiduesGroupIDPrefix, SubGroupID)
  808 
  809             # Add sub group to residues group...
  810             PyMOLObjectNames["Chains"][ChainID][ResiduesSubGroupID] = ResiduesSubGroupName
  811             PyMOLObjectNames["Chains"][ChainID][ResiduesGroupMembersID].append(ResiduesSubGroupName)
  812 
  813             # Initialize sub group members...
  814             ResiduesSubGroupMembersID = "%s%sGroupMembers" % (ResiduesGroupIDPrefix, SubGroupID)
  815             PyMOLObjectNames["Chains"][ChainID][ResiduesSubGroupMembersID] = []
  816 
  817             # Add sub group members to subgroup...
  818             for MemberType in ["Residues", "Surface"]:
  819                 MemberID = re.sub("_", "", MemberType)
  820 
  821                 SubGroupMemberName = "%s.%s" % (ResiduesSubGroupName, MemberType)
  822                 SubGroupMemberID = "%s%s%s" % (ResiduesGroupIDPrefix, SubGroupID, MemberID)
  823 
  824                 PyMOLObjectNames["Chains"][ChainID][SubGroupMemberID] = SubGroupMemberName
  825                 PyMOLObjectNames["Chains"][ChainID][ResiduesSubGroupMembersID].append(SubGroupMemberName)
  826 
  827     if GetChainAloneContainsSurfacesStatus(FileIndex, ChainID):
  828         # Setup cavity and surface groups...
  829         for SubGroupType in ["Cavities", "Surface"]:
  830             SubGroupName = "%s.%s" % (ChainAloneGroupName, SubGroupType)
  831             SubGroupID = "ChainAlone%sGroup" % (SubGroupType)
  832             SubGroupMembersID = "%sMembers" % (SubGroupID)
  833 
  834             PyMOLObjectNames["Chains"][ChainID][SubGroupID] = SubGroupName
  835             PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"].append(SubGroupName)
  836 
  837             PyMOLObjectNames["Chains"][ChainID][SubGroupMembersID] = []
  838 
  839             # Setup a generic color surface...
  840             SurfaceName = "%s.Surface" % (SubGroupName)
  841             SurfaceID = "%sSurface" % (SubGroupID)
  842             PyMOLObjectNames["Chains"][ChainID][SurfaceID] = SurfaceName
  843             PyMOLObjectNames["Chains"][ChainID][SubGroupMembersID].append(SurfaceName)
  844 
  845             if GetChainAloneSurfaceChainStatus(FileIndex, ChainID):
  846                 # Setup hydrophobicity surface...
  847                 HydrophobicSurfaceName = "%s.Hydrophobicity" % (SubGroupName)
  848                 HydrophobicSurfaceID = "%sHydrophobicSurface" % (SubGroupID)
  849                 PyMOLObjectNames["Chains"][ChainID][HydrophobicSurfaceID] = HydrophobicSurfaceName
  850                 PyMOLObjectNames["Chains"][ChainID][SubGroupMembersID].append(HydrophobicSurfaceName)
  851 
  852                 # Setup hydrophobicity and charge surface...
  853                 HydrophobicChargeSurfaceName = "%s.Hydrophobicity_Charge" % (SubGroupName)
  854                 HydrophobicChargeSurfaceID = "%sHydrophobicChargeSurface" % (SubGroupID)
  855                 PyMOLObjectNames["Chains"][ChainID][HydrophobicChargeSurfaceID] = HydrophobicChargeSurfaceName
  856                 PyMOLObjectNames["Chains"][ChainID][SubGroupMembersID].append(HydrophobicChargeSurfaceName)
  857 
  858             if GetChainAloneSurfaceChainElectrostaticsStatus(FileIndex, ChainID):
  859                 # Setup electrostatics group...
  860                 ElectrostaticsGroupName = "%s.Vacuum_Electrostatics" % (SubGroupName)
  861                 ElectrostaticsGroupID = "%sElectrostaticsGroup" % (SubGroupID)
  862                 ElectrostaticsGroupMembersID = "%sElectrostaticsGroupMembers" % (SubGroupID)
  863 
  864                 PyMOLObjectNames["Chains"][ChainID][ElectrostaticsGroupID] = ElectrostaticsGroupName
  865                 PyMOLObjectNames["Chains"][ChainID][SubGroupMembersID].append(ElectrostaticsGroupName)
  866 
  867                 # Setup electrostatics group members...
  868                 PyMOLObjectNames["Chains"][ChainID][ElectrostaticsGroupMembersID] = []
  869 
  870                 for MemberType in ["Chain", "Contact_Potential", "Map", "Legend"]:
  871                     MemberID = re.sub("_", "", MemberType)
  872 
  873                     Name = "%s.%s" % (ElectrostaticsGroupName, MemberType)
  874                     NameID = "%s%s" % (ElectrostaticsGroupID, MemberID)
  875 
  876                     PyMOLObjectNames["Chains"][ChainID][NameID] = Name
  877                     PyMOLObjectNames["Chains"][ChainID][ElectrostaticsGroupMembersID].append(Name)
  878 
  879     # Setup solvent and inorganic objects for chain...
  880     for NameID in ["Solvent", "Inorganic"]:
  881         Name = "%s.%s" % (ChainGroupName, NameID)
  882         PyMOLObjectNames["Chains"][ChainID][NameID] = Name
  883         PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(Name)
  884 
  885 
  886 def SetupPyMOLObjectNamesForLigand(FileIndex, PyMOLObjectNames, ChainID, LigandID):
  887     """Stetup groups and objects for ligand."""
  888 
  889     PyMOLObjectNames["Ligands"][ChainID][LigandID] = {}
  890 
  891     ChainGroupName = PyMOLObjectNames["Chains"][ChainID]["ChainGroup"]
  892 
  893     # Setup a chain level ligand group...
  894     ChainLigandGroupName = "%s.Ligand%s" % (ChainGroupName, LigandID)
  895     PyMOLObjectNames["Ligands"][ChainID][LigandID]["ChainLigandGroup"] = ChainLigandGroupName
  896     PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(ChainLigandGroupName)
  897 
  898     PyMOLObjectNames["Ligands"][ChainID][LigandID]["ChainLigandGroupMembers"] = []
  899 
  900     # Set up ligand group and its members...
  901     GroupName = "%s.Ligand" % (ChainLigandGroupName)
  902     GroupNameID = "LigandGroup"
  903     GroupMembersID = "LigandGroupMembers"
  904 
  905     PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupNameID] = GroupName
  906     PyMOLObjectNames["Ligands"][ChainID][LigandID]["ChainLigandGroupMembers"].append(GroupName)
  907 
  908     LigandName = "%s.Ligand" % (GroupName)
  909     LigandNameID = "Ligand"
  910     PyMOLObjectNames["Ligands"][ChainID][LigandID][LigandNameID] = LigandName
  911 
  912     PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID] = []
  913     PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID].append(LigandName)
  914 
  915     # Add ball and stick...
  916     BallAndStickName = "%s.BallAndStick" % (GroupName)
  917     BallAndStickID = "LigandBallAndStick"
  918     PyMOLObjectNames["Ligands"][ChainID][LigandID][BallAndStickID] = BallAndStickName
  919     PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID].append(BallAndStickName)
  920 
  921 
  922 def RetrieveInfilesInfo():
  923     """Retrieve information for input files."""
  924 
  925     InfilesInfo = {}
  926 
  927     InfilesInfo["InfilesNames"] = []
  928     InfilesInfo["InfilesRoots"] = []
  929     InfilesInfo["ChainsAndLigandsInfo"] = []
  930 
  931     for Infile in OptionsInfo["InfilesNames"]:
  932         FileDir, FileName, FileExt = MiscUtil.ParseFileName(Infile)
  933         InfileRoot = FileName
  934 
  935         ChainsAndLigandInfo = PyMOLUtil.GetChainsAndLigandsInfo(Infile, InfileRoot)
  936 
  937         InfilesInfo["InfilesNames"].append(Infile)
  938         InfilesInfo["InfilesRoots"].append(InfileRoot)
  939         InfilesInfo["ChainsAndLigandsInfo"].append(ChainsAndLigandInfo)
  940 
  941     OptionsInfo["InfilesInfo"] = InfilesInfo
  942 
  943 
  944 def RetrieveRefFileInfo():
  945     """Retrieve information for ref file."""
  946 
  947     RefFileInfo = {}
  948     if not OptionsInfo["Align"]:
  949         OptionsInfo["RefFileInfo"] = RefFileInfo
  950         return
  951 
  952     RefFile = OptionsInfo["RefFileName"]
  953 
  954     FileDir, FileName, FileExt = MiscUtil.ParseFileName(RefFile)
  955     RefFileRoot = FileName
  956 
  957     if re.match("^FirstInputFile$", OptionsInfo["AlignRefFile"], re.I):
  958         ChainsAndLigandInfo = OptionsInfo["InfilesInfo"]["ChainsAndLigandsInfo"][0]
  959     else:
  960         MiscUtil.PrintInfo("\nRetrieving chain and ligand information for alignment reference file %s..." % RefFile)
  961         ChainsAndLigandInfo = PyMOLUtil.GetChainsAndLigandsInfo(RefFile, RefFileRoot)
  962 
  963     RefFileInfo["RefFileName"] = RefFile
  964     RefFileInfo["RefFileRoot"] = RefFileRoot
  965     RefFileInfo["PyMOLObjectName"] = "AlignRef_%s" % RefFileRoot
  966     RefFileInfo["ChainsAndLigandsInfo"] = ChainsAndLigandInfo
  967 
  968     OptionsInfo["RefFileInfo"] = RefFileInfo
  969 
  970 
  971 def ProcessChainAndLigandIDs():
  972     """Process specified chain and ligand IDs for infiles."""
  973 
  974     OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"] = []
  975 
  976     for FileIndex in range(0, len(OptionsInfo["InfilesInfo"]["InfilesNames"])):
  977         MiscUtil.PrintInfo(
  978             "\nProcessing specified chain and ligand IDs for input file %s..."
  979             % OptionsInfo["InfilesInfo"]["InfilesNames"][FileIndex]
  980         )
  981 
  982         ChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["ChainsAndLigandsInfo"][FileIndex]
  983         SpecifiedChainsAndLigandsInfo = PyMOLUtil.ProcessChainsAndLigandsOptionsInfo(
  984             ChainsAndLigandsInfo, "-c, --chainIDs", OptionsInfo["ChainIDs"], "-l, --ligandIDs", OptionsInfo["LigandIDs"]
  985         )
  986         ProcessResidueTypesAndSurfaceOptions(FileIndex, SpecifiedChainsAndLigandsInfo)
  987         OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"].append(SpecifiedChainsAndLigandsInfo)
  988 
  989         CheckPresenceOfValidLigandIDs(ChainsAndLigandsInfo, SpecifiedChainsAndLigandsInfo)
  990 
  991 
  992 def ProcessResidueTypesAndSurfaceOptions(FileIndex, SpecifiedChainsAndLigandsInfo):
  993     """Process residue types and surface options for chains."""
  994 
  995     SpecifiedChainsAndLigandsInfo["ChainSurfaces"] = {}
  996     SpecifiedChainsAndLigandsInfo["SurfaceChain"] = {}
  997     SpecifiedChainsAndLigandsInfo["SurfaceChainElectrostatics"] = {}
  998 
  999     SpecifiedChainsAndLigandsInfo["ResidueTypesChain"] = {}
 1000 
 1001     # Load infile...
 1002     Infile = OptionsInfo["InfilesInfo"]["InfilesNames"][FileIndex]
 1003     MolName = OptionsInfo["InfilesInfo"]["InfilesRoots"][FileIndex]
 1004     pymol.cmd.load(Infile, MolName)
 1005 
 1006     for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
 1007         AminoAcidsPresent = PyMOLUtil.AreAminoAcidResiduesPresent(MolName, ChainID)
 1008 
 1009         # Process surfaces for chains...
 1010         if re.match("^auto$", OptionsInfo["SurfaceChain"], re.I):
 1011             SurfaceChain = True if AminoAcidsPresent else False
 1012         else:
 1013             SurfaceChain = True if re.match("^yes$", OptionsInfo["SurfaceChain"], re.I) else False
 1014         SpecifiedChainsAndLigandsInfo["SurfaceChain"][ChainID] = SurfaceChain
 1015 
 1016         if re.match("^auto$", OptionsInfo["SurfaceChainElectrostatics"], re.I):
 1017             SurfaceChainElectrostatics = True if AminoAcidsPresent else False
 1018         else:
 1019             SurfaceChainElectrostatics = (
 1020                 True if re.match("^yes$", OptionsInfo["SurfaceChainElectrostatics"], re.I) else False
 1021             )
 1022         SpecifiedChainsAndLigandsInfo["SurfaceChainElectrostatics"][ChainID] = SurfaceChainElectrostatics
 1023 
 1024         # A generic color surface is always created...
 1025         ChainSurfaces = True
 1026         SpecifiedChainsAndLigandsInfo["ChainSurfaces"][ChainID] = ChainSurfaces
 1027 
 1028         # Process residue types for chains...
 1029         if re.match("^auto$", OptionsInfo["ResidueTypesChain"], re.I):
 1030             ResidueTypesChain = True if AminoAcidsPresent else False
 1031         else:
 1032             ResidueTypesChain = True if re.match("^yes$", OptionsInfo["ResidueTypesChain"], re.I) else False
 1033         SpecifiedChainsAndLigandsInfo["ResidueTypesChain"][ChainID] = ResidueTypesChain
 1034 
 1035     # Delete loaded object...
 1036     pymol.cmd.delete(MolName)
 1037 
 1038 
 1039 def GetChainAloneResidueTypesStatus(FileIndex, ChainID):
 1040     """Get status of residue types for chain alone object."""
 1041 
 1042     Status = (
 1043         True
 1044         if OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]["ResidueTypesChain"][ChainID]
 1045         else False
 1046     )
 1047 
 1048     return Status
 1049 
 1050 
 1051 def GetChainAloneContainsSurfacesStatus(FileIndex, ChainID):
 1052     """Get status of surfaces present in chain alone object."""
 1053 
 1054     return OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]["ChainSurfaces"][ChainID]
 1055 
 1056 
 1057 def GetChainAloneSurfaceChainStatus(FileIndex, ChainID):
 1058     """Get status of hydrophobic surfaces for chain alone object."""
 1059 
 1060     return OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]["SurfaceChain"][ChainID]
 1061 
 1062 
 1063 def GetChainAloneSurfaceChainElectrostaticsStatus(FileIndex, ChainID):
 1064     """Get status of electrostatics surfaces for chain alone object."""
 1065 
 1066     return OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]["SurfaceChainElectrostatics"][ChainID]
 1067 
 1068 
 1069 def CheckPresenceOfValidLigandIDs(ChainsAndLigandsInfo, SpecifiedChainsAndLigandsInfo):
 1070     """Check presence of valid ligand IDs."""
 1071 
 1072     MiscUtil.PrintInfo("\nSpecified chain IDs: %s" % (", ".join(SpecifiedChainsAndLigandsInfo["ChainIDs"])))
 1073 
 1074     for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
 1075         if len(SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID]):
 1076             MiscUtil.PrintInfo(
 1077                 "Chain ID: %s; Specified LigandIDs: %s"
 1078                 % (ChainID, ", ".join(SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID]))
 1079             )
 1080         else:
 1081             MiscUtil.PrintInfo("Chain IDs: %s; Specified LigandIDs: None" % (ChainID))
 1082             MiscUtil.PrintWarning(
 1083                 "No valid ligand IDs found for chain ID, %s. PyMOL groups and objects related to ligand and binding pockect won't be created."
 1084                 % (ChainID)
 1085             )
 1086 
 1087 
 1088 def RetrieveFirstChainID(FileIndex):
 1089     """Get first chain ID."""
 1090 
 1091     ChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["ChainsAndLigandsInfo"][FileIndex]
 1092 
 1093     FirstChainID = None
 1094     if len(ChainsAndLigandsInfo["ChainIDs"]):
 1095         FirstChainID = ChainsAndLigandsInfo["ChainIDs"][0]
 1096 
 1097     return FirstChainID
 1098 
 1099 
 1100 def ProcessResidueTypes():
 1101     """Process residue types."""
 1102 
 1103     ResidueTypesNamesInfo, ResidueTypesParamsInfo = PyMOLUtil.ProcessResidueTypesOptionsInfo(
 1104         "-r, --residueTypes", OptionsInfo["ResidueTypes"]
 1105     )
 1106     OptionsInfo["ResidueTypesNames"] = ResidueTypesNamesInfo
 1107     OptionsInfo["ResidueTypesParams"] = ResidueTypesParamsInfo
 1108 
 1109 
 1110 def ProcessSurfaceAtomTypesColors():
 1111     """Process surface atom types colors."""
 1112 
 1113     AtomTypesColorNamesInfo = PyMOLUtil.ProcessSurfaceAtomTypesColorsOptionsInfo(
 1114         "--surfaceAtomTypesColors", OptionsInfo["SurfaceAtomTypesColors"]
 1115     )
 1116     OptionsInfo["AtomTypesColorNames"] = AtomTypesColorNamesInfo
 1117 
 1118 
 1119 def ProcessOptions():
 1120     """Process and validate command line arguments and options."""
 1121 
 1122     MiscUtil.PrintInfo("Processing options...")
 1123 
 1124     # Validate options...
 1125     ValidateOptions()
 1126 
 1127     OptionsInfo["Align"] = True if re.match("^Yes$", Options["--align"], re.I) else False
 1128     OptionsInfo["AlignMethod"] = Options["--alignMethod"].lower()
 1129     OptionsInfo["AlignMode"] = Options["--alignMode"]
 1130 
 1131     OptionsInfo["AllowEmptyObjects"] = True if re.match("^Yes$", Options["--allowEmptyObjects"], re.I) else False
 1132 
 1133     OptionsInfo["CavityCulled"] = True if re.match("^Yes$", Options["--cavityCulled"], re.I) else False
 1134     OptionsInfo["SurfaceCavityMode"] = 2 if OptionsInfo["CavityCulled"] else 1
 1135 
 1136     OptionsInfo["CavityCullSize"] = float(Options["--cavityCullSize"])
 1137     OptionsInfo["CavityCutoff"] = float(Options["--cavityCutoff"])
 1138     OptionsInfo["CavityRadius"] = float(Options["--cavityRadius"])
 1139 
 1140     OptionsInfo["Infiles"] = Options["--infiles"]
 1141     OptionsInfo["InfilesNames"] = Options["--infileNames"]
 1142 
 1143     OptionsInfo["AlignRefFile"] = Options["--alignRefFile"]
 1144     if re.match("^FirstInputFile$", Options["--alignRefFile"], re.I):
 1145         OptionsInfo["RefFileName"] = OptionsInfo["InfilesNames"][0]
 1146     else:
 1147         OptionsInfo["RefFileName"] = Options["--alignRefFile"]
 1148 
 1149     OptionsInfo["IgnoreHydrogens"] = True if re.match("^Yes$", Options["--ignoreHydrogens"], re.I) else False
 1150 
 1151     OptionsInfo["Overwrite"] = Options["--overwrite"]
 1152     OptionsInfo["PMLOut"] = True if re.match("^Yes$", Options["--PMLOut"], re.I) else False
 1153 
 1154     OptionsInfo["Outfile"] = Options["--outfile"]
 1155     FileDir, FileName, FileExt = MiscUtil.ParseFileName(OptionsInfo["Outfile"])
 1156     OptionsInfo["PSEOut"] = False
 1157     if re.match("^pml$", FileExt, re.I):
 1158         OptionsInfo["PMLOutfile"] = OptionsInfo["Outfile"]
 1159         OptionsInfo["PMEOutfile"] = re.sub(".pml$", ".pme", OptionsInfo["Outfile"])
 1160     elif re.match("^pse$", FileExt, re.I):
 1161         OptionsInfo["PSEOut"] = True
 1162         OptionsInfo["PSEOutfile"] = OptionsInfo["Outfile"]
 1163         OptionsInfo["PMLOutfile"] = re.sub(".pse$", ".pml", OptionsInfo["Outfile"])
 1164         if os.path.exists(OptionsInfo["PMLOutfile"]) and (not OptionsInfo["Overwrite"]):
 1165             MiscUtil.PrintError(
 1166                 'The intermediate output file to be generated, %s, already exist. Use option "--ov" or "--overwrite" and try again.'
 1167                 % OptionsInfo["PMLOutfile"]
 1168             )
 1169 
 1170     OptionsInfo["LabelFontID"] = int(Options["--labelFontID"])
 1171 
 1172     OptionsInfo["ResidueTypesChain"] = Options["--residueTypesChain"]
 1173     OptionsInfo["ResidueTypes"] = Options["--residueTypes"]
 1174     ProcessResidueTypes()
 1175 
 1176     OptionsInfo["SurfaceChain"] = Options["--surfaceChain"]
 1177     OptionsInfo["SurfaceChainElectrostatics"] = Options["--surfaceChainElectrostatics"]
 1178 
 1179     OptionsInfo["SurfaceChainComplex"] = True if re.match("^Yes$", Options["--surfaceChainComplex"], re.I) else False
 1180     OptionsInfo["SurfaceComplex"] = True if re.match("^Yes$", Options["--surfaceComplex"], re.I) else False
 1181 
 1182     # Retrieve surface colors for generic surfaces..
 1183     SurfaceColors = re.sub(" ", "", Options["--surfaceColors"])
 1184     SurfaceColorsWords = SurfaceColors.split(",")
 1185     if len(SurfaceColorsWords) != 2:
 1186         MiscUtil.PrintError(
 1187             'The number of comma delinited color names, %d, specified using "--surfaceColors" option, "%s",  must be a 2.'
 1188             % (len(SurfaceColorsWords), Options["--surfaceColors"])
 1189         )
 1190     OptionsInfo["SurfaceColors"] = SurfaceColors
 1191     OptionsInfo["SurfaceCavityColor"] = SurfaceColorsWords[0]
 1192     OptionsInfo["SurfaceColor"] = SurfaceColorsWords[1]
 1193 
 1194     OptionsInfo["SurfaceColorPalette"] = Options["--surfaceColorPalette"]
 1195     OptionsInfo["SurfaceAtomTypesColors"] = Options["--surfaceAtomTypesColors"]
 1196     ProcessSurfaceAtomTypesColors()
 1197 
 1198     OptionsInfo["SurfaceTransparency"] = float(Options["--surfaceTransparency"])
 1199 
 1200     RetrieveInfilesInfo()
 1201     RetrieveRefFileInfo()
 1202 
 1203     OptionsInfo["ChainIDs"] = Options["--chainIDs"]
 1204     OptionsInfo["LigandIDs"] = Options["--ligandIDs"]
 1205 
 1206     ProcessChainAndLigandIDs()
 1207 
 1208 
 1209 def RetrieveOptions():
 1210     """Retrieve command line arguments and options."""
 1211 
 1212     # Get options...
 1213     global Options
 1214     Options = docopt(_docoptUsage_)
 1215 
 1216     # Set current working directory to the specified directory...
 1217     WorkingDir = Options["--workingdir"]
 1218     if WorkingDir:
 1219         os.chdir(WorkingDir)
 1220 
 1221     # Handle examples option...
 1222     if "--examples" in Options and Options["--examples"]:
 1223         MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_))
 1224         sys.exit(0)
 1225 
 1226 
 1227 def ValidateOptions():
 1228     """Validate option values."""
 1229 
 1230     MiscUtil.ValidateOptionTextValue("--align", Options["--align"], "yes no")
 1231     MiscUtil.ValidateOptionTextValue("--alignMethod", Options["--alignMethod"], "align cealign super")
 1232     MiscUtil.ValidateOptionTextValue("--alignMode", Options["--alignMode"], "FirstChain Complex")
 1233 
 1234     MiscUtil.ValidateOptionTextValue("--allowEmptyObjects", Options["--allowEmptyObjects"], "yes no")
 1235 
 1236     MiscUtil.ValidateOptionTextValue("--cavityCulled", Options["--cavityCulled"], "yes no")
 1237     MiscUtil.ValidateOptionFloatValue("--cavityCullSize", Options["--cavityCullSize"], {">": 0.0})
 1238     MiscUtil.ValidateOptionFloatValue("--cavityCutoff", Options["--cavityCutoff"], {">": 0.0})
 1239     MiscUtil.ValidateOptionFloatValue("--cavityRadius", Options["--cavityRadius"], {">": 0.0})
 1240 
 1241     # Expand infiles to handle presence of multiple input files...
 1242     InfileNames = MiscUtil.ExpandFileNames(Options["--infiles"], ",")
 1243     if not len(InfileNames):
 1244         MiscUtil.PrintError('No input files specified for "-i, --infiles" option')
 1245 
 1246     # Validate file extensions...
 1247     for Infile in InfileNames:
 1248         MiscUtil.ValidateOptionFilePath("-i, --infiles", Infile)
 1249         MiscUtil.ValidateOptionFileExt("-i, --infiles", Infile, "pdb cif")
 1250         MiscUtil.ValidateOptionsDistinctFileNames("-i, --infiles", Infile, "-o, --outfile", Options["--outfile"])
 1251     Options["--infileNames"] = InfileNames
 1252 
 1253     MiscUtil.ValidateOptionFileExt("-o, --outfile", Options["--outfile"], "pml pse")
 1254     MiscUtil.ValidateOptionsOutputFileOverwrite(
 1255         "-o, --outfile", Options["--outfile"], "--overwrite", Options["--overwrite"]
 1256     )
 1257 
 1258     if re.match("^yes$", Options["--align"], re.I):
 1259         if not re.match("^FirstInputFile$", Options["--alignRefFile"], re.I):
 1260             AlignRefFile = Options["--alignRefFile"]
 1261             MiscUtil.ValidateOptionFilePath("--alignRefFile", AlignRefFile)
 1262             MiscUtil.ValidateOptionFileExt("--alignRefFile", AlignRefFile, "pdb cif")
 1263             MiscUtil.ValidateOptionsDistinctFileNames(
 1264                 "--AlignRefFile", AlignRefFile, "-o, --outfile", Options["--outfile"]
 1265             )
 1266 
 1267     MiscUtil.ValidateOptionTextValue("--ignoreHydrogens", Options["--ignoreHydrogens"], "yes no")
 1268 
 1269     MiscUtil.ValidateOptionTextValue("--PMLOut", Options["--PMLOut"], "yes no")
 1270     MiscUtil.ValidateOptionIntegerValue("--labelFontID", Options["--labelFontID"], {})
 1271 
 1272     MiscUtil.ValidateOptionTextValue("--residueTypesChain", Options["--residueTypesChain"], "yes no auto")
 1273 
 1274     MiscUtil.ValidateOptionTextValue("--surfaceChain", Options["--surfaceChain"], "yes no auto")
 1275     MiscUtil.ValidateOptionTextValue("--surfaceComplex", Options["--surfaceComplex"], "yes no")
 1276     MiscUtil.ValidateOptionTextValue("--surfaceChainComplex", Options["--surfaceChainComplex"], "yes no")
 1277     MiscUtil.ValidateOptionTextValue(
 1278         "--surfaceChainElectrostatics", Options["--surfaceChainElectrostatics"], "yes no auto"
 1279     )
 1280 
 1281     MiscUtil.ValidateOptionTextValue(
 1282         "--surfaceColorPalette", Options["--surfaceColorPalette"], "RedToWhite WhiteToGreen"
 1283     )
 1284     MiscUtil.ValidateOptionFloatValue("--surfaceTransparency", Options["--surfaceTransparency"], {">=": 0.0, "<=": 1.0})
 1285 
 1286 
 1287 # Setup a usage string for docopt...
 1288 _docoptUsage_ = """
 1289 PyMOLVisualizeCavities.py - Visualize cavities and pockets in macromolecules
 1290 
 1291 Usage:
 1292     PyMOLVisualizeCavities.py [--align <yes or no>] [--alignMethod <align, cealign, super>]
 1293                                     [--alignMode <FirstChain or Complex>] [--alignRefFile <filename>]
 1294                                     [--allowEmptyObjects <yes or no>] [--cavityCulled <Yes or No>]
 1295                                     [--cavityCullSize <number>] [--cavityCutoff <number>] [--cavityRadius <number>]
 1296                                     [--chainIDs <First, All or ID1,ID2...>] [--labelFontID <number>]
 1297                                     [--ignoreHydrogens <yes or no>] [--ligandIDs <Largest, All or ID1,ID2...> ] [--PMLOut <yes or no>]
 1298                                     [--residueTypes <Type,Color,ResNames,...>] [--residueTypesChain <yes or no>]
 1299                                     [--surfaceChain <yes or no>] [--surfaceChainElectrostatics <yes or no>]
 1300                                     [--surfaceChainComplex <yes or no>] [--surfaceComplex <yes or no>]
 1301                                     [--surfaceAtomTypesColors <ColorType,ColorSpec,...>]
 1302                                     [--surfaceColors <ColorName1,ColorName2>] [--surfaceColorPalette <RedToWhite or WhiteToGreen>]
 1303                                     [--surfaceTransparency <number>] [--overwrite] [-w <dir>] -i <infile1,infile2,infile3...> -o <outfile>
 1304     PyMOLVisualizeCavities.py -h | --help | -e | --examples
 1305 
 1306 Description:
 1307     Generate PyMOL visualization files for viewing cavities and pockets in
 1308     macromolecules including proteins and nucleic acids.
 1309 
 1310     The supported input file format are: PDB (.pdb), CIF (.cif)
 1311 
 1312     The supported output file formats are: PyMOL script file (.pml), PyMOL session
 1313     file (.pse)
 1314 
 1315     A variety of PyMOL groups and objects may be  created for visualization of
 1316     cavities and pockets in macromolecules. These groups and objects correspond
 1317     to complexes, surfaces, chains, ligands, inorganics, cavities, and pockets.
 1318     A complete hierarchy of all possible PyMOL groups and objects is shown below:
 1319     
 1320         <PDBFileRoot>
 1321             .Complex
 1322                 .Complex
 1323                 .Surface
 1324             .Chain<ID>
 1325                 .Complex
 1326                     .Complex
 1327                     .Surface
 1328                 .Chain
 1329                     .Chain
 1330                     .Residues
 1331                         .Aromatic
 1332                             .Residues
 1333                             .Surface
 1334                         .Hydrophobic
 1335                             .Residues
 1336                             .Surface
 1337                         .Polar
 1338                             .Residues
 1339                             .Surface
 1340                         .Positively_Charged
 1341                             .Residues
 1342                             .Surface
 1343                         .Negatively_Charged
 1344                             .Residues
 1345                             .Surface
 1346                         .Other
 1347                             .Residues
 1348                             .Surface
 1349                     .Cavities
 1350                         .Surface
 1351                         .Hydrophobicity
 1352                         .Hydrophobicity_Charge
 1353                         .Vacuum_Electrostatics
 1354                             .Contact_Potentials
 1355                             .Map
 1356                             .Legend
 1357                     .Surface
 1358                         .Surface
 1359                         .Hydrophobicity
 1360                         .Hydrophobicity_Charge
 1361                         .Vacuum_Electrostatics
 1362                             .Contact_Potentials
 1363                             .Map
 1364                             .Legend
 1365                 .Solvent
 1366                 .Inorganic
 1367                 .Ligand<ID>
 1368                     .Ligand
 1369                         .Ligand
 1370                         .BallAndStick
 1371                 .Ligand<ID>
 1372                     .Ligand
 1373                         ... ... ...
 1374             .Chain<ID>
 1375                 ... ... ...
 1376                 .Ligand<ID>
 1377                     ... ... ...
 1378                 .Ligand<ID>
 1379                     ... ... ...
 1380             .Chain<ID>
 1381                 ... ... ...
 1382         <PDBFileRoot>
 1383             .Complex
 1384                 ... ... ...
 1385             .Chain<ID>
 1386                 ... ... ...
 1387                 .Ligand<ID>
 1388                     ... ... ...
 1389                 .Ligand<ID>
 1390                     ... ... ...
 1391             .Chain<ID>
 1392                 ... ... ...
 1393     
 1394     The hydrophobic and electrostatic surfaces are not created for complete complex
 1395     and chain complex in input file(s) by default. A word to the wise: The creation of
 1396     surface objects may slow down loading of PML file and generation of PSE file, based
 1397     on the size of input complexes. The generation of PSE file may also fail.
 1398 
 1399 Options:
 1400     -a, --align <yes or no>  [default: no]
 1401         Align input files to a reference file before visualization.
 1402     --alignMethod <align, cealign, super>  [default: super]
 1403         Alignment methodology to use for aligning input files to a
 1404         reference file.
 1405     --alignMode <FirstChain or Complex>  [default: FirstChain]
 1406         Portion of input and reference files to use for spatial alignment of
 1407         input files against reference file.  Possible values: FirstChain or
 1408         Complex.
 1409         
 1410         The FirstChain mode allows alignment of the first chain in each input
 1411         file to the first chain in the reference file along with moving the rest
 1412         of the complex to coordinate space of the reference file. The complete
 1413         complex in each input file is aligned to the complete complex in reference
 1414         file for the Complex mode.
 1415     --alignRefFile <filename>  [default: FirstInputFile]
 1416         Reference input file name. The default is to use the first input file
 1417         name specified using '-i, --infiles' option.
 1418     --allowEmptyObjects <yes or no>  [default: no]
 1419         Allow creation of empty PyMOL objects corresponding to solvent and
 1420         inorganic atom selections across chains and ligands in input file(s). By
 1421         default, the empty objects are marked for deletion.
 1422     --cavityCulled <Yes or No>  [default: Yes]
 1423         Cull cavities and pockets. The cavities and pockets are culled by default.
 1424         This value is used to set of PyMOL parameter surface_cavity_mode as
 1425         shown below:
 1426         
 1427             No: 1 (Cavities and Pockets Only)
 1428             Yes: 2 (Cavities and Pockets Culled)
 1429         
 1430     --cavityCullSize <number>  [default: 2.0]
 1431         Approximate dimension of the cavity in Angstroms for detecting cavities and
 1432         pockets in the interior of a macromolecule. The higher value makes PyMOL less
 1433         sensitive to detection of smaller cavities.
 1434     --cavityCutoff <number>  [default: 3.0]
 1435         Cavity cutoff in terms of number of solvent radii for detecting cavities
 1436         and pockets. This value is used to set value of PyMOL parameter
 1437         surface_cavity_cutoff.
 1438     --cavityRadius <number>  [default: 5.0]
 1439         Cavity detection radius in terms of number of solvent radii for detecting
 1440         cavities and pockets. The detection of larger pockets is ignored for lower
 1441         value for the cavity radius. This value is used to set of PyMOL parameter
 1442         surface_cavity_radius.
 1443     -c, --chainIDs <First, All or ID1,ID2...>  [default: First]
 1444         List of chain IDs to use for visualizing macromolecules. Possible values:
 1445         First, All, or a comma delimited list of chain IDs. The default is to use the
 1446         chain ID for the first chain in each input file.
 1447     -e, --examples
 1448         Print examples.
 1449     -h, --help
 1450         Print this help message.
 1451     -i, --infiles <infile1,infile2,infile3...>
 1452         Input file names.
 1453     --ignoreHydrogens <yes or no>  [default: yes]
 1454         Ignore hydrogens for ligand views.
 1455     --labelFontID <number>  [default: 7]
 1456         Font ID for drawing labels. Default: 7 (Sans Bold). Valid values: 5 to 16.
 1457         The specified value must be a valid PyMOL font ID. No validation is
 1458         performed. The complete lists of valid font IDs is available at:
 1459         pymolwiki.org/index.php/Label_font_id. Examples: 5 - Sans;
 1460         7 - Sans Bold; 9 - Serif; 10 - Serif Bold.
 1461     -l, --ligandIDs <Largest, All or ID1,ID2...>  [default: All]
 1462         List of ligand IDs to show in chains during visualizing of cavities in
 1463         macromolecules. Possible values: Largest, All, or a comma delimited
 1464         list of ligand IDs. The default is to show all ligands present in all or
 1465         specified chains in each input file.
 1466         
 1467         Ligands are identified using organic selection operator available in PyMOL.
 1468         It'll also  identify buffer molecules as ligands. The largest ligand contains
 1469         the highest number of heavy atoms.
 1470     -o, --outfile <outfile>
 1471         Output file name.
 1472     -p, --PMLOut <yes or no>  [default: yes]
 1473         Save PML file during generation of PSE file.
 1474     -r, --residueTypes <Type,Color,ResNames,...>  [default: auto]
 1475         Residue types, colors, and names to generate for residue groups during
 1476         '--residueTypesChain' option. It is only valid for amino acids.
 1477         
 1478         It is a triplet of comma delimited list of amino acid residues type, residues
 1479         color, and a space delimited list three letter residue names. 
 1480         
 1481         The default values for residue type, color, and name triplets  are shown
 1482         below:
 1483             
 1484             Aromatic,brightorange,HIS PHE TRP TYR,
 1485             Hydrophobic,orange,ALA GLY VAL LEU ILE PRO MET,
 1486             Polar,palegreen,ASN GLN SER THR CYS,
 1487             Positively_Charged,marine,ARG LYS,
 1488             Negatively_Charged,red,ASP GLU
 1489             
 1490         The color name must be a valid PyMOL name. No validation is performed.
 1491         An amino acid name may appear across multiple residue types. All other
 1492         residues are grouped under 'Other'.
 1493     --residueTypesChain <yes or no>  [default: auto]
 1494         Chain residue types. The residue groups are generated using residue types,
 1495         colors, and names specified by '--residueTypes' option. It is only valid for
 1496         amino acids.  By default, the residue type groups are automatically created
 1497         for chains containing amino acids and skipped for chains only containing
 1498         nucleic acids.
 1499     --surfaceChain <yes or no>  [default: auto]
 1500         Surfaces around individual chain colored by hydrophobicity alone and
 1501         both hydrophobicity and charge. The hydrophobicity surface is colored
 1502         at residue level using Eisenberg hydrophobicity scale for residues and color
 1503         gradient specified by '--surfaceColorPalette' option. The  hydrophobicity and
 1504         charge surface is colored [ Ref 140 ] at atom level using colors specified for
 1505         groups of atoms by '--surfaceAtomTypesColors' option. This scheme allows
 1506         simultaneous mapping of hyrophobicity and charge values on the surfaces.
 1507         
 1508         This option is only valid for amino acids. By default, both surfaces are
 1509         automatically created for chains containing amino acids and skipped for
 1510         chains containing only nucleic acids.
 1511         
 1512         In addition, generic surfaces colored by '--surfaceColor' are always created
 1513         for chain residues containing amino acids and nucleic acids.
 1514     --surfaceChainElectrostatics <yes or no>  [default: no]
 1515         Vacuum electrostatics contact potential surface around individual
 1516         chain. A word to the wise from PyMOL documentation: The computed protein
 1517         contact potentials are only qualitatively useful, due to short cutoffs,
 1518         truncation, and lack of solvent "screening".
 1519         
 1520         This option is only valid for amino acids. By default, the electrostatics surface
 1521         is automatically created for chains containing amino acids and
 1522         skipped for chains containing only nucleic acids.
 1523     --surfaceChainComplex <yes or no>  [default: no]
 1524         Hydrophobic surface around chain complex. The  surface is colored by
 1525         hydrophobicity. It is only valid for amino acids.
 1526     --surfaceComplex <yes or no>  [default: no]
 1527         Hydrophobic surface around complete complex. The  surface is colored by
 1528         hydrophobicity. It is only valid for amino acids.
 1529     --surfaceAtomTypesColors <ColorType,ColorSpec,...>  [default: auto]
 1530         Atom colors for generating surfaces colored by hyrophobicity and charge
 1531         around chains and pockets in proteins. It's a pairwise comma delimited list
 1532         of atom color type and color specification for goups of atoms.
 1533         
 1534         The default values for color types [ Ref 140 ] along wth color specifications
 1535         are shown below: 
 1536             
 1537             HydrophobicAtomsColor, yellow,
 1538             NegativelyChargedAtomsColor, red,
 1539             PositivelyChargedAtomsColor, blue,
 1540             OtherAtomsColor, gray90
 1541             
 1542         The color names must be valid PyMOL names.
 1543         
 1544         The color values may also be specified as space delimited RGB triplets:
 1545              
 1546             HydrophobicAtomsColor, 0.95 0.78 0.0,
 1547             NegativelyChargedAtomsColor, 1.0 0.4 0.4,
 1548             PositivelyChargedAtomsColor, 0.2 0.5 0.8,
 1549             OtherAtomsColor, 0.95 0.95 0.95
 1550             
 1551     --surfaceColors <ColorName1,ColorName2>  [default: salmon,lightblue]
 1552         Color names for surfaces around cavities and chains. These colors are not
 1553         used for surfaces and cavities colored by hydrophobicity and charge. The
 1554         color names must be valid PyMOL names.
 1555     --surfaceColorPalette <RedToWhite or WhiteToGreen>  [default: RedToWhite]
 1556         Color palette for hydrophobic surfaces around chains and pockets in proteins.
 1557         Possible values: RedToWhite or WhiteToGreen from most hydrophobic amino
 1558         acid to least hydrophobic. The colors values for amino acids are taken from
 1559         color_h script available as part of the Script Library at PyMOL Wiki.
 1560     --surfaceTransparency <number>  [default: 0.25]
 1561         Surface transparency for molecular surfaces.
 1562     --overwrite
 1563         Overwrite existing files.
 1564     -w, --workingdir <dir>
 1565         Location of working directory which defaults to the current directory.
 1566 
 1567 Examples:
 1568     To visualize cavities in the first chain along with the largest ligand in the
 1569     first chain, solvents, and inorganics, in a PDB file, and generate a PML
 1570     file, type:
 1571 
 1572         % PyMOLVisualizeCavities.py -i Sample4.pdb -o Sample4.pml
 1573 
 1574     To visualize cavities in all chain along with all ligands, solvents,
 1575     and inorganics, in a PDB file, and generate a PML file, type:
 1576 
 1577         % PyMOLVisualizeCavities.py -c All -l All -i Sample4.pdb
 1578           -o Sample4.pml
 1579 
 1580     To visualize cavities in the first chain at a specific cavity radius and cutoff
 1581     using specifc colors for surfaces corresponding to cavities and non-cavities,
 1582     and generate a PML file, type:
 1583 
 1584         % PyMOLVisualizeCavities.py  --cavityRadius 3 --cavityCutoff 5
 1585            --surfaceColors "red,blue" -i Sample4.pdb -o Sample4.pml
 1586 
 1587     To visualize cavities in the first chain along with the largest ligand in the
 1588     first chain, solvents, and inorganics, in PDB files, along with aligning first
 1589     chain in each input file to the first chain inand generate a PML file, type:
 1590 
 1591         % PyMOLVisualizeCavities.py --align yes
 1592           -i "Sample5.pdb,Sample6.pdb,Sample7.pdb"
 1593           -o SampleOut.pml
 1594 
 1595 Author:
 1596     Manish Sud(msud@san.rr.com)
 1597 
 1598 See also:
 1599     DownloadPDBFiles.pl, PyMOLVisualizeCryoEMDensity.py,
 1600     PyMOLVisualizeElectronDensity.py, PyMOLVisualizeInterfaces.py
 1601     PyMOLVisualizeMacromolecules.py, PyMOLVisualizeSurfaceAndBuriedResidues.py
 1602 
 1603 Copyright:
 1604     Copyright (C) 2026 Manish Sud. All rights reserved.
 1605 
 1606     The functionality available in this script is implemented using PyMOL, a
 1607     molecular visualization system on an open source foundation originally
 1608     developed by Warren DeLano.
 1609 
 1610     This file is part of MayaChemTools.
 1611 
 1612     MayaChemTools is free software; you can redistribute it and/or modify it under
 1613     the terms of the GNU Lesser General Public License as published by the Free
 1614     Software Foundation; either version 3 of the License, or (at your option) any
 1615     later version.
 1616 
 1617 """
 1618 
 1619 if __name__ == "__main__":
 1620     main()