MayaChemTools

    1 #!/bin/env python
    2 #
    3 # File: PyMOLVisualizeFpockets.py
    4 # Author: Manish Sud <msud@san.rr.com>
    5 #
    6 # Author: Manish Sud
    7 #
    8 # Collaborators: Joann Prescott-Roy and Pat Walters
    9 #
   10 # Copyright (C) 2026 Manish Sud. All rights reserved.
   11 #
   12 # The functionality available in this script is implemented using PyMOL, a
   13 # molecular visualization system on an open source foundation originally
   14 # developed by Warren DeLano.
   15 #
   16 # This file is part of MayaChemTools.
   17 #
   18 # MayaChemTools is free software; you can redistribute it and/or modify it under
   19 # the terms of the GNU Lesser General Public License as published by the Free
   20 # Software Foundation; either version 3 of the License, or (at your option) any
   21 # later version.
   22 #
   23 # MayaChemTools is distributed in the hope that it will be useful, but without
   24 # any warranty; without even the implied warranty of merchantability of fitness
   25 # for a particular purpose.  See the GNU Lesser General Public License for more
   26 # details.
   27 #
   28 # You should have received a copy of the GNU Lesser General Public License
   29 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
   30 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
   31 # Boston, MA, 02111-1307, USA.
   32 #
   33 #
   34 
   35 from __future__ import print_function
   36 
   37 import os
   38 import sys
   39 import time
   40 import re
   41 import glob
   42 import shutil
   43 
   44 # PyMOL imports...
   45 try:
   46     import pymol
   47 
   48     # Finish launching PyMOL in  a command line mode for batch processing (-c)
   49     # along with the following options:  disable loading of pymolrc and plugins (-k);
   50     # suppress start up messages (-q)
   51     pymol.finish_launching(["pymol", "-ckq"])
   52 except ImportError as ErrMsg:
   53     sys.stderr.write("\nFailed to import PyMOL module/package: %s\n" % ErrMsg)
   54     sys.stderr.write("Check/update your PyMOL environment and try again.\n\n")
   55     sys.exit(1)
   56 
   57 # MayaChemTools imports...
   58 sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), "..", "lib", "Python"))
   59 try:
   60     from docopt import docopt
   61     import MiscUtil
   62     import PyMOLUtil
   63 except ImportError as ErrMsg:
   64     sys.stderr.write("\nFailed to import MayaChemTools module/package: %s\n" % ErrMsg)
   65     sys.stderr.write("Check/update your MayaChemTools environment and try again.\n\n")
   66     sys.exit(1)
   67 
   68 ScriptName = os.path.basename(sys.argv[0])
   69 Options = {}
   70 OptionsInfo = {}
   71 
   72 
   73 def main():
   74     """Start execution of the script"""
   75 
   76     MiscUtil.PrintInfo(
   77         "\n%s (PyMOL v%s; MayaChemTools v%s; %s): Starting...\n"
   78         % (ScriptName, pymol.cmd.get_version()[0], MiscUtil.GetMayaChemToolsVersion(), time.asctime())
   79     )
   80 
   81     (WallClockTime, ProcessorTime) = MiscUtil.GetWallClockAndProcessorTime()
   82 
   83     # Retrieve command line arguments and options...
   84     RetrieveOptions()
   85 
   86     # Process and validate command line arguments and options...
   87     ProcessOptions()
   88 
   89     # Perform actions required by the script...
   90     GenerateFpocketsVisualization()
   91 
   92     MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName)
   93     MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime))
   94 
   95 
   96 def GenerateFpocketsVisualization():
   97     """Generate fpockets visualization."""
   98 
   99     Outfile = OptionsInfo["PMLOutfilePath"]
  100     OutFH = open(Outfile, "w")
  101     if OutFH is None:
  102         MiscUtil.PrintError("Failed to open output fie %s " % Outfile)
  103 
  104     MiscUtil.PrintInfo("\nGenerating file %s..." % Outfile)
  105 
  106     # Setup header...
  107     WritePMLHeader(OutFH, ScriptName)
  108     WritePyMOLParameters(OutFH)
  109 
  110     if OptionsInfo["Align"]:
  111         WriteAlignReference(OutFH)
  112 
  113     # Setup view for each input file...
  114     FirstComplex = True
  115     FirstComplexFirstChainName = None
  116     for FileIndex in range(0, len(OptionsInfo["InfilesInfo"]["InfilesNames"])):
  117         # Setup PyMOL object names...
  118         PyMOLObjectNames = SetupPyMOLObjectNames(FileIndex)
  119 
  120         # Setup complex view...
  121         WriteComplexView(OutFH, FileIndex, PyMOLObjectNames, FirstComplex)
  122 
  123         # Setup chain and pocket views...
  124         SpecifiedChainsAndPocketsInfo = OptionsInfo["FpocketInfilesInfo"]["SpecifiedChainsAndPocketsInfo"][FileIndex]
  125         FirstChain = True
  126         for ChainID in SpecifiedChainsAndPocketsInfo["ChainIDs"]:
  127             if FirstComplex and FirstChain:
  128                 FirstComplexFirstChainName = PyMOLObjectNames["Chains"][ChainID]["ChainAlone"]
  129 
  130             WriteChainView(OutFH, FileIndex, PyMOLObjectNames, ChainID)
  131 
  132             # Setup fpocket views...
  133             FirstPocket = True
  134             PocketNum = 0
  135             for PocketID in SpecifiedChainsAndPocketsInfo["PocketIDs"][ChainID]:
  136                 PocketNum += 1
  137                 WriteChainPocketView(OutFH, FileIndex, PyMOLObjectNames, ChainID, PocketID, PocketNum)
  138 
  139                 # Set up pocket level group...
  140                 Enable, Action = [False, "close"]
  141                 if FirstPocket:
  142                     FirstPocket = False
  143                     Enable, Action = [True, "open"]
  144                 GenerateAndWritePMLForGroup(
  145                     OutFH,
  146                     PyMOLObjectNames["Pockets"][ChainID][PocketID]["ChainPocketGroup"],
  147                     PyMOLObjectNames["Pockets"][ChainID][PocketID]["ChainPocketGroupMembers"],
  148                     Enable,
  149                     Action,
  150                 )
  151 
  152             # Setup Chain level group...
  153             Enable, Action = [False, "close"]
  154             if FirstChain:
  155                 FirstChain = False
  156                 Enable, Action = [True, "open"]
  157             GenerateAndWritePMLForGroup(
  158                 OutFH,
  159                 PyMOLObjectNames["Chains"][ChainID]["ChainGroup"],
  160                 PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"],
  161                 Enable,
  162                 Action,
  163             )
  164 
  165         # Set up complex level group...
  166         Enable, Action = [False, "close"]
  167         if FirstComplex:
  168             FirstComplex = False
  169             Enable, Action = [True, "open"]
  170         GenerateAndWritePMLForGroup(
  171             OutFH, PyMOLObjectNames["PDBGroup"], PyMOLObjectNames["PDBGroupMembers"], Enable, Action
  172         )
  173 
  174     if OptionsInfo["Align"]:
  175         DeleteAlignReference(OutFH)
  176 
  177     if FirstComplexFirstChainName is not None:
  178         OutFH.write("""\ncmd.orient("%s", animate = -1)\n""" % FirstComplexFirstChainName)
  179     else:
  180         OutFH.write("""\ncmd.orient("visible", animate = -1)\n""")
  181 
  182     OutFH.close()
  183 
  184     CopyPDBFilesForPML()
  185 
  186 
  187 def WritePMLHeader(OutFH, ScriptName):
  188     """Write out PML header."""
  189 
  190     HeaderInfo = PyMOLUtil.SetupPMLHeaderInfo(ScriptName)
  191     OutFH.write("%s\n" % HeaderInfo)
  192 
  193 
  194 def WritePyMOLParameters(OutFH):
  195     """Write out PyMOL global parameters."""
  196 
  197     PMLCmds = []
  198     PMLCmds.append("""cmd.set("transparency", %.2f, "", 0)""" % (OptionsInfo["SurfaceTransparency"]))
  199     PMLCmds.append("""cmd.set("label_font_id", %s)""" % (OptionsInfo["LabelFontID"]))
  200     PML = "\n".join(PMLCmds)
  201 
  202     OutFH.write("""\n""\n"Setting up PyMOL gobal parameters..."\n""\n""")
  203     OutFH.write("%s\n" % PML)
  204 
  205 
  206 def WriteAlignReference(OutFH):
  207     """Setup object for alignment reference"""
  208 
  209     RefFileInfo = OptionsInfo["RefFileInfo"]
  210     RefFile = RefFileInfo["RefFileName"]
  211     RefName = RefFileInfo["PyMOLObjectName"]
  212 
  213     PMLCmds = []
  214     PMLCmds.append("""cmd.load("%s", "%s")""" % (RefFile, RefName))
  215     PMLCmds.append("""cmd.hide("everything", "%s")""" % (RefName))
  216     PMLCmds.append("""cmd.disable("%s")""" % (RefName))
  217     PML = "\n".join(PMLCmds)
  218 
  219     OutFH.write("""\n""\n"Loading %s and setting up view for align reference..."\n""\n""" % RefFile)
  220     OutFH.write("%s\n" % PML)
  221 
  222 
  223 def WriteAlignComplex(OutFH, FileIndex, FpocketComplexMode, PyMOLObjectNames):
  224     """Setup alignment of complex to reference"""
  225 
  226     RefFileInfo = OptionsInfo["RefFileInfo"]
  227     RefName = RefFileInfo["PyMOLObjectName"]
  228 
  229     if FpocketComplexMode:
  230         ComplexName = PyMOLObjectNames["FpocketComplex"]
  231     else:
  232         ComplexName = PyMOLObjectNames["InitialComplex"]
  233 
  234     if re.match("^FirstChain$", OptionsInfo["AlignMode"], re.I):
  235         RefFirstChainID = RefFileInfo["ChainsAndLigandsInfo"]["ChainIDs"][0]
  236         RefAlignSelection = "%s and chain %s" % (RefName, RefFirstChainID)
  237 
  238         ComplexFirstChainID = RetrieveFirstChainID(FileIndex, FpocketComplexMode)
  239         ComplexAlignSelection = "%s and chain %s" % (ComplexName, ComplexFirstChainID)
  240     else:
  241         RefAlignSelection = RefName
  242         ComplexAlignSelection = ComplexName
  243 
  244     PML = PyMOLUtil.SetupPMLForAlignment(OptionsInfo["AlignMethod"], RefAlignSelection, ComplexAlignSelection)
  245     OutFH.write("""\n""\n"Aligning %s against reference %s ..."\n""\n""" % (ComplexAlignSelection, RefAlignSelection))
  246     OutFH.write("%s\n" % PML)
  247 
  248 
  249 def DeleteAlignReference(OutFH):
  250     """Delete alignment reference object."""
  251 
  252     RefName = OptionsInfo["RefFileInfo"]["PyMOLObjectName"]
  253     OutFH.write("""\n""\n"Deleting alignment reference object %s..."\n""\n""" % RefName)
  254     OutFH.write("""cmd.delete("%s")\n""" % RefName)
  255 
  256 
  257 def WriteComplexView(OutFH, FileIndex, PyMOLObjectNames, FirstComplex):
  258     """Write out PML for viewing polymer complex."""
  259 
  260     # Setup initial complex...
  261     Infile = OptionsInfo["InfilesInfo"]["InfilesNames"][FileIndex]
  262     PML = PyMOLUtil.SetupPMLForPolymerComplexView(PyMOLObjectNames["InitialComplex"], Infile, True)
  263     OutFH.write("""\n""\n"Loading %s and setting up view for complex..."\n""\n""" % Infile)
  264     OutFH.write("%s\n" % PML)
  265 
  266     if OptionsInfo["Align"]:
  267         # No need to align initial complex on to itself...
  268         FpocketComplexMode = False
  269         if not (re.match("^FirstInputFile$", OptionsInfo["AlignRefFile"], re.I) and FirstComplex):
  270             WriteAlignComplex(OutFH, FileIndex, FpocketComplexMode, PyMOLObjectNames)
  271 
  272     # Setup fpocket complex...
  273     Infile = OptionsInfo["FpocketInfilesInfo"]["InfilesNames"][FileIndex]
  274     PML = PyMOLUtil.SetupPMLForPolymerComplexView(PyMOLObjectNames["FpocketComplex"], Infile, True)
  275 
  276     # Modify default complex view for fpocket complex...
  277     PMLModify = SetupPMLModifyDefaultPolymerComplexView(PyMOLObjectNames["FpocketComplex"])
  278 
  279     OutFH.write("""\n""\n"Loading %s and setting up view for complex..."\n""\n""" % Infile)
  280     OutFH.write("%s\n%s\n" % (PML, PMLModify))
  281 
  282     if OptionsInfo["Align"]:
  283         # No need to align initial complex on to itself...
  284         FpocketComplexMode = True
  285         if not (re.match("^FirstInputFile$", OptionsInfo["AlignRefFile"], re.I) and FirstComplex):
  286             WriteAlignComplex(OutFH, FileIndex, FpocketComplexMode, PyMOLObjectNames)
  287 
  288     # Setup complex group...
  289     GenerateAndWritePMLForGroup(
  290         OutFH, PyMOLObjectNames["ComplexGroup"], PyMOLObjectNames["ComplexGroupMembers"], False, "close"
  291     )
  292 
  293 
  294 def WriteChainView(OutFH, FileIndex, PyMOLObjectNames, ChainID):
  295     """Write out PML for viewing chain."""
  296 
  297     OutFH.write("""\n""\n"Setting up views for chain %s..."\n""\n""" % ChainID)
  298 
  299     # Setup chain complex group view...
  300     WriteChainComplexViews(OutFH, FileIndex, PyMOLObjectNames, ChainID)
  301 
  302     # Setup chain view...
  303     WriteChainAloneViews(OutFH, FileIndex, PyMOLObjectNames, ChainID)
  304 
  305 
  306 def WriteChainComplexViews(OutFH, FileIndex, PyMOLObjectNames, ChainID):
  307     """Write chain complex views."""
  308 
  309     # Setup chain complex...
  310     ChainComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
  311     PML = SetupPMLForFpocketChainComplexView(
  312         FileIndex, ChainComplexName, PyMOLObjectNames["FpocketComplex"], ChainID, True
  313     )
  314 
  315     # Modify default complex view for fpocket chain complex...
  316     PMLModify = SetupPMLModifyDefaultPolymerComplexView(ChainComplexName)
  317     OutFH.write("%s\n%s\n" % (PML, PMLModify))
  318 
  319     # Setup chain complex group...
  320     GenerateAndWritePMLForGroup(
  321         OutFH,
  322         PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroup"],
  323         PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"],
  324         False,
  325         "close",
  326     )
  327 
  328 
  329 def WriteChainAloneViews(OutFH, FileIndex, PyMOLObjectNames, ChainID):
  330     """Write individual chain views."""
  331 
  332     ChainComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
  333 
  334     # Setup chain view...
  335     ChainName = PyMOLObjectNames["Chains"][ChainID]["ChainAlone"]
  336     PML = PyMOLUtil.SetupPMLForPolymerChainView(ChainName, ChainComplexName, True)
  337     OutFH.write("\n%s\n" % PML)
  338 
  339     if GetChainAloneContainsSurfacesStatus(FileIndex, ChainID):
  340         # Setup a generic color surface...
  341         PML = PyMOLUtil.SetupPMLForSurfaceView(
  342             PyMOLObjectNames["Chains"][ChainID]["ChainAloneSurface"],
  343             ChainName,
  344             Enable=False,
  345             Color=OptionsInfo["SurfaceColor"],
  346         )
  347         OutFH.write("\n%s\n" % PML)
  348 
  349         if GetChainAloneSurfaceChainStatus(FileIndex, ChainID):
  350             # Setup surface colored by hydrophobicity...
  351             PML = PyMOLUtil.SetupPMLForHydrophobicSurfaceView(
  352                 PyMOLObjectNames["Chains"][ChainID]["ChainAloneHydrophobicSurface"],
  353                 ChainName,
  354                 ColorPalette=OptionsInfo["SurfaceColorPalette"],
  355                 Enable=False,
  356             )
  357             OutFH.write("\n%s\n" % PML)
  358 
  359             # Setup surface colored by hyrdophobicity and charge...
  360             PML = PyMOLUtil.SetupPMLForHydrophobicAndChargeSurfaceView(
  361                 PyMOLObjectNames["Chains"][ChainID]["ChainAloneHydrophobicChargeSurface"],
  362                 ChainName,
  363                 OptionsInfo["AtomTypesColorNames"]["HydrophobicAtomsColor"],
  364                 OptionsInfo["AtomTypesColorNames"]["NegativelyChargedAtomsColor"],
  365                 OptionsInfo["AtomTypesColorNames"]["PositivelyChargedAtomsColor"],
  366                 OptionsInfo["AtomTypesColorNames"]["OtherAtomsColor"],
  367                 Enable=False,
  368                 DisplayAs=None,
  369             )
  370             OutFH.write("\n%s\n" % PML)
  371 
  372         # Setup surface group...
  373         GenerateAndWritePMLForGroup(
  374             OutFH,
  375             PyMOLObjectNames["Chains"][ChainID]["ChainAloneSurfaceGroup"],
  376             PyMOLObjectNames["Chains"][ChainID]["ChainAloneSurfaceGroupMembers"],
  377             True,
  378             "open",
  379         )
  380 
  381     # Setup chain group...
  382     GenerateAndWritePMLForGroup(
  383         OutFH,
  384         PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroup"],
  385         PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"],
  386         True,
  387         "close",
  388     )
  389 
  390 
  391 def WriteChainPocketView(OutFH, FileIndex, PyMOLObjectNames, ChainID, PocketID, PocketNum):
  392     """Write out PML for viewing pocket in a chain."""
  393 
  394     OutFH.write("""\n""\n"Setting up views for pocket %s in chain %s..."\n""\n""" % (PocketID, ChainID))
  395 
  396     FpocketComplexName = PyMOLObjectNames["FpocketComplex"]
  397 
  398     # Setup pocket...
  399     PML = SetupPMLForFPocketView(
  400         PyMOLObjectNames["Pockets"][ChainID][PocketID]["Pocket"], FpocketComplexName, ChainID, PocketID, PocketNum, True
  401     )
  402     OutFH.write("%s\n" % PML)
  403 
  404     # Setup pocket residues...
  405     ChainsAndPocketsInfo = OptionsInfo["FpocketInfilesInfo"]["ChainsAndPocketsInfo"][FileIndex]
  406     PocketResNums = ChainsAndPocketsInfo["PocketResNums"][ChainID][PocketID]
  407     PocketResiduesName = PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketResidues"]
  408     PML = SetupPMLForFPocketResiduesView(
  409         PocketResiduesName, FpocketComplexName, ChainID, PocketID, PocketNum, PocketResNums, True
  410     )
  411     OutFH.write("%s\n" % PML)
  412 
  413     # Setup pocket surfaces and group...
  414     if GetPocketContainsSurfaceStatus(FileIndex, ChainID, PocketID):
  415         # Setup a generic color surface...
  416         PML = PyMOLUtil.SetupPMLForSurfaceView(
  417             PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketSurface"],
  418             PocketResiduesName,
  419             Enable=False,
  420             Color=OptionsInfo["SurfaceColor"],
  421         )
  422         OutFH.write("\n%s\n" % PML)
  423 
  424         if GetPocketSurfaceChainStatus(FileIndex, ChainID, PocketID):
  425             # Setup surface colored by hydrophobicity...
  426             PML = PyMOLUtil.SetupPMLForHydrophobicSurfaceView(
  427                 PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketHydrophobicitySurface"],
  428                 PocketResiduesName,
  429                 ColorPalette=OptionsInfo["SurfaceColorPalette"],
  430                 Enable=False,
  431             )
  432             OutFH.write("\n%s\n" % PML)
  433 
  434             # Setup surface colored by hyrdophobicity and charge...
  435             PML = PyMOLUtil.SetupPMLForHydrophobicAndChargeSurfaceView(
  436                 PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketHydrophobicityChargeSurface"],
  437                 PocketResiduesName,
  438                 OptionsInfo["AtomTypesColorNames"]["HydrophobicAtomsColor"],
  439                 OptionsInfo["AtomTypesColorNames"]["NegativelyChargedAtomsColor"],
  440                 OptionsInfo["AtomTypesColorNames"]["PositivelyChargedAtomsColor"],
  441                 OptionsInfo["AtomTypesColorNames"]["OtherAtomsColor"],
  442                 Enable=False,
  443                 DisplayAs=None,
  444             )
  445             OutFH.write("\n%s\n" % PML)
  446 
  447         # Setup surface group...
  448         GenerateAndWritePMLForGroup(
  449             OutFH,
  450             PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketSurfaceGroup"],
  451             PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketSurfaceGroupMembers"],
  452             True,
  453             "open",
  454         )
  455 
  456     # Setup pocket group...
  457     GenerateAndWritePMLForGroup(
  458         OutFH,
  459         PyMOLObjectNames["Pockets"][ChainID][PocketID]["ChainPocketGroup"],
  460         PyMOLObjectNames["Pockets"][ChainID][PocketID]["ChainPocketGroupMembers"],
  461         True,
  462         "open",
  463     )
  464 
  465 
  466 def SetupPMLForFpocketChainComplexView(FileIndex, Name, Selection, ChainName, Enable=True):
  467     """Setup PML commands for creating a polymer chain complex view
  468     including fockets for the chain."""
  469 
  470     PMLCmds = []
  471 
  472     # Include fpockets as spheres for the chain complex view...
  473     ChainsAndPocketsInfo = OptionsInfo["FpocketInfilesInfo"]["ChainsAndPocketsInfo"][FileIndex]
  474     PocketIDs = ChainsAndPocketsInfo["PocketIDs"][ChainName]
  475 
  476     PMLCmds.append(
  477         """cmd.create("%s", "((%s and chain %s) or (%s and (resn STP and resi %s)))")"""
  478         % (Name, Selection, ChainName, Selection, "+".join(PocketIDs))
  479     )
  480     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
  481     PMLCmds.append("""cmd.show("cartoon", "%s")""" % (Name))
  482     PMLCmds.append("""util.cba(33, "%s", _self = cmd)""" % (Name))
  483     PMLCmds.append("""cmd.show("sticks", "(organic and (%s))")""" % (Name))
  484 
  485     PMLCmds.append("""cmd.show("nonbonded", "(solvent and (%s))")""" % (Name))
  486     PMLCmds.append("""cmd.show("nonbonded", "(inorganic and (%s))")""" % (Name))
  487 
  488     PMLCmds.append("""cmd.show("lines", "%s")""" % (Name))
  489 
  490     PMLCmds.append("""cmd.set_bond("valence", "1", "%s", quiet = 1)""" % (Name))
  491     PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(Name, Enable))
  492 
  493     PML = "\n".join(PMLCmds)
  494 
  495     return PML
  496 
  497 
  498 def SetupPMLModifyDefaultPolymerComplexView(Name):
  499     """Setup PML to modify default polymer complex view for fpocket."""
  500 
  501     PMLCmds = []
  502     PMLCmds.append("""cmd.hide("lines", "%s")""" % (Name))
  503     PMLCmds.append("""cmd.show("lines", "(polymer and (%s))")""" % (Name))
  504     PMLCmds.append("""cmd.show("lines", "(solvent and (%s))")""" % (Name))
  505     PMLCmds.append("""cmd.show("spheres", "(inorganic and (%s))")""" % (Name))
  506     PMLCmds.append("""cmd.set("sphere_scale", "%s", "(inorganic and (%s))")""" % (OptionsInfo["SphereScale"], Name))
  507     PMLCmds.append(
  508         """cmd.set("sphere_transparency", "%s", "(inorganic and (%s))")""" % (OptionsInfo["SphereTransparency"], Name)
  509     )
  510 
  511     PML = "\n".join(PMLCmds)
  512 
  513     return PML
  514 
  515 
  516 def SetupPMLForFPocketView(FpocketName, FpocketComplexName, ChainID, PocketID, PocketNum, Enable=True):
  517     """Setup PML  to visualize fpocket spheres using fpocket complex."""
  518 
  519     PMLCmds = []
  520     PMLCmds.append(
  521         """cmd.create("%s", "(%s and (resn STP and resi %s))")""" % (FpocketName, FpocketComplexName, PocketID)
  522     )
  523     if PocketNum == 1:
  524         # Skip color index of 1: It is set to black. Use pocket one color name...
  525         PMLCmds.append("""cmd.color(%s, "%s")""" % (OptionsInfo["PocketNumOneColor"], FpocketName))
  526     else:
  527         PMLCmds.append("""cmd.color(%s, "%s")""" % (PocketNum, FpocketName))
  528     PMLCmds.append("""cmd.show("spheres", "%s")""" % (FpocketName))
  529     PMLCmds.append("""cmd.set("sphere_scale", "%s", "%s")""" % (OptionsInfo["SphereScale"], FpocketName))
  530     PMLCmds.append("""cmd.set("sphere_transparency", "%s", "%s")""" % (OptionsInfo["SphereTransparency"], FpocketName))
  531 
  532     PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(FpocketName, Enable))
  533 
  534     PML = "\n".join(PMLCmds)
  535     return PML
  536 
  537 
  538 def SetupPMLForFPocketResiduesView(Name, FpocketComplexName, ChainID, PocketID, PocketNum, PocketResNums, Enable=True):
  539     """Setup PML to visualize fpocket residues."""
  540 
  541     PMLCmds = []
  542 
  543     PMLCmds.append(
  544         """cmd.create("%s", "(%s and (chain %s) and (resi %s))")"""
  545         % (Name, FpocketComplexName, ChainID, "+".join(PocketResNums))
  546     )
  547     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
  548 
  549     # Setup pocket residue labels...
  550     if OptionsInfo["PocketLabel"]:
  551         if OptionsInfo["ThreeLetterPocketLabelType"]:
  552             LabelFormat = """"%s-%s"%(resn,resi)"""
  553             PMLCmds.append("""cmd.label("(name CA+C1*+C1' and (byres(%s)))", \'\'\'%s\'\'\')""" % (Name, LabelFormat))
  554         else:
  555             PMLCmds.append("""cmd.label("byca(%s)", "oneletter+resi")""" % (Name))
  556 
  557     PocketColor = OptionsInfo["PocketNumOneColor"] if PocketNum == 1 else PocketNum
  558 
  559     if OptionsInfo["PocketColorByPocketNum"]:
  560         # Setup color of pocket residues...
  561         PMLCmds.append(PyMOLUtil.SetupPMLForDeepColoring(Name, PocketColor))
  562 
  563         # Setup color of pocket residue labels...
  564         PMLCmds.append("""cmd.set("label_color", %s, "%s")""" % (PocketColor, Name))
  565     else:
  566         PMLCmds.append("""util.cbag("%s", _self = cmd)""" % (Name))
  567 
  568     PMLCmds.append("""cmd.show("lines", "%s")""" % (Name))
  569 
  570     PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(Name, Enable))
  571 
  572     PML = "\n".join(PMLCmds)
  573     return PML
  574 
  575 
  576 def GenerateAndWritePMLForGroup(OutFH, GroupName, GroupMembers, Enable=False, Action="close"):
  577     """Generate and write PML for group."""
  578 
  579     PML = PyMOLUtil.SetupPMLForGroup(GroupName, GroupMembers, Enable, Action)
  580     OutFH.write("""\n""\n"Setting up group %s..."\n""\n""" % GroupName)
  581     OutFH.write("%s\n" % PML)
  582 
  583 
  584 def WritePMLToCheckAndDeleteEmptyObjects(OutFH, ObjectName, ParentObjectName=None):
  585     """Write PML to check and delete empty PyMOL objects."""
  586 
  587     if ParentObjectName is None:
  588         PML = """CheckAndDeleteEmptyObjects("%s")""" % (ObjectName)
  589     else:
  590         PML = """CheckAndDeleteEmptyObjects("%s", "%s")""" % (ObjectName, ParentObjectName)
  591 
  592     OutFH.write("%s\n" % PML)
  593 
  594 
  595 def SetupPyMOLObjectNames(FileIndex):
  596     """Setup hierarchy of PyMOL groups and objects for pocket centric views of
  597     chains and pockets present in input file.
  598     """
  599 
  600     PyMOLObjectNames = {}
  601     PyMOLObjectNames["Chains"] = {}
  602     PyMOLObjectNames["Pockets"] = {}
  603 
  604     # Setup groups and objects for complex...
  605     SetupPyMOLObjectNamesForComplex(FileIndex, PyMOLObjectNames)
  606 
  607     # Setup groups and objects for chains using fpockets info...
  608     SpecifiedChainsAndPocketsInfo = OptionsInfo["FpocketInfilesInfo"]["SpecifiedChainsAndPocketsInfo"][FileIndex]
  609     for ChainID in SpecifiedChainsAndPocketsInfo["ChainIDs"]:
  610         SetupPyMOLObjectNamesForChain(FileIndex, PyMOLObjectNames, ChainID)
  611 
  612         # Setup groups and objects for pocket...
  613         for PocketID in SpecifiedChainsAndPocketsInfo["PocketIDs"][ChainID]:
  614             SetupPyMOLObjectNamesForPocket(FileIndex, PyMOLObjectNames, ChainID, PocketID)
  615 
  616     return PyMOLObjectNames
  617 
  618 
  619 def SetupPyMOLObjectNamesForComplex(FileIndex, PyMOLObjectNames):
  620     """Setup groups and objects for complex."""
  621 
  622     PDBFileRoot = OptionsInfo["InfilesInfo"]["InfilesRoots"][FileIndex]
  623 
  624     PDBGroupName = "%s" % PDBFileRoot
  625     PyMOLObjectNames["PDBGroup"] = PDBGroupName
  626     PyMOLObjectNames["PDBGroupMembers"] = []
  627 
  628     ComplexGroupName = "%s.Complex" % PyMOLObjectNames["PDBGroup"]
  629     PyMOLObjectNames["ComplexGroup"] = ComplexGroupName
  630     PyMOLObjectNames["ComplexGroupMembers"] = []
  631 
  632     PyMOLObjectNames["PDBGroupMembers"].append(ComplexGroupName)
  633 
  634     PyMOLObjectNames["InitialComplex"] = "%s.Initial_Complex" % ComplexGroupName
  635     PyMOLObjectNames["FpocketComplex"] = "%s.Fpocket_Complex" % ComplexGroupName
  636 
  637     PyMOLObjectNames["ComplexGroupMembers"].append(PyMOLObjectNames["InitialComplex"])
  638     PyMOLObjectNames["ComplexGroupMembers"].append(PyMOLObjectNames["FpocketComplex"])
  639 
  640 
  641 def SetupPyMOLObjectNamesForChain(FileIndex, PyMOLObjectNames, ChainID):
  642     """Setup groups and objects for chain."""
  643 
  644     PDBGroupName = PyMOLObjectNames["PDBGroup"]
  645 
  646     PyMOLObjectNames["Chains"][ChainID] = {}
  647     PyMOLObjectNames["Pockets"][ChainID] = {}
  648 
  649     # Set up chain group and chain objects...
  650     ChainGroupName = "%s.Chain%s" % (PDBGroupName, ChainID)
  651     PyMOLObjectNames["Chains"][ChainID]["ChainGroup"] = ChainGroupName
  652     PyMOLObjectNames["PDBGroupMembers"].append(ChainGroupName)
  653     PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"] = []
  654 
  655     # Setup chain complex group and objects...
  656     ChainComplexGroupName = "%s.Complex" % (ChainGroupName)
  657     PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroup"] = ChainComplexGroupName
  658     PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(ChainComplexGroupName)
  659 
  660     PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"] = []
  661 
  662     Name = "%s.Complex" % (ChainComplexGroupName)
  663     PyMOLObjectNames["Chains"][ChainID]["ChainComplex"] = Name
  664     PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"].append(Name)
  665 
  666     # Setup up a group for individual chains...
  667     ChainAloneGroupName = "%s.Chain" % (ChainGroupName)
  668     PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroup"] = ChainAloneGroupName
  669     PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(ChainAloneGroupName)
  670 
  671     PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"] = []
  672 
  673     Name = "%s.Chain" % (ChainAloneGroupName)
  674     PyMOLObjectNames["Chains"][ChainID]["ChainAlone"] = Name
  675     PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"].append(Name)
  676 
  677     if GetChainAloneContainsSurfacesStatus(FileIndex, ChainID):
  678         # Setup a surface group and add it to chain alone group...
  679         SurfaceGroupName = "%s.Surface" % (ChainAloneGroupName)
  680         PyMOLObjectNames["Chains"][ChainID]["ChainAloneSurfaceGroup"] = SurfaceGroupName
  681         PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"].append(SurfaceGroupName)
  682 
  683         PyMOLObjectNames["Chains"][ChainID]["ChainAloneSurfaceGroupMembers"] = []
  684 
  685         # Setup a generic color surface...
  686         Name = "%s.Surface" % (SurfaceGroupName)
  687         PyMOLObjectNames["Chains"][ChainID]["ChainAloneSurface"] = Name
  688         PyMOLObjectNames["Chains"][ChainID]["ChainAloneSurfaceGroupMembers"].append(Name)
  689 
  690         if GetChainAloneSurfaceChainStatus(FileIndex, ChainID):
  691             # Setup hydrophobicity surface...
  692             Name = "%s.Hydrophobicity" % (SurfaceGroupName)
  693             PyMOLObjectNames["Chains"][ChainID]["ChainAloneHydrophobicSurface"] = Name
  694             PyMOLObjectNames["Chains"][ChainID]["ChainAloneSurfaceGroupMembers"].append(Name)
  695 
  696             # Setup hydrophobicity and charge surface...
  697             Name = "%s.Hydrophobicity_Charge" % (SurfaceGroupName)
  698             PyMOLObjectNames["Chains"][ChainID]["ChainAloneHydrophobicChargeSurface"] = Name
  699             PyMOLObjectNames["Chains"][ChainID]["ChainAloneSurfaceGroupMembers"].append(Name)
  700 
  701 
  702 def SetupPyMOLObjectNamesForPocket(FileIndex, PyMOLObjectNames, ChainID, PocketID):
  703     """Stetup groups and objects for pocket."""
  704 
  705     PyMOLObjectNames["Pockets"][ChainID][PocketID] = {}
  706 
  707     ChainGroupName = PyMOLObjectNames["Chains"][ChainID]["ChainGroup"]
  708 
  709     # Setup a chain level pocket group...
  710     ChainPocketGroupName = SetupChainPocketGroupName(FileIndex, ChainGroupName, ChainID, PocketID)
  711 
  712     PyMOLObjectNames["Pockets"][ChainID][PocketID]["ChainPocketGroup"] = ChainPocketGroupName
  713     PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(ChainPocketGroupName)
  714 
  715     PyMOLObjectNames["Pockets"][ChainID][PocketID]["ChainPocketGroupMembers"] = []
  716 
  717     # Setup fpocket...
  718     Name = "%s.Fpocket" % (ChainPocketGroupName)
  719     PyMOLObjectNames["Pockets"][ChainID][PocketID]["Pocket"] = Name
  720     PyMOLObjectNames["Pockets"][ChainID][PocketID]["ChainPocketGroupMembers"].append(Name)
  721 
  722     # Setup fpocket residues...
  723     Name = "%s.Residues" % (ChainPocketGroupName)
  724     PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketResidues"] = Name
  725     PyMOLObjectNames["Pockets"][ChainID][PocketID]["ChainPocketGroupMembers"].append(Name)
  726 
  727     if GetPocketContainsSurfaceStatus(FileIndex, ChainID, PocketID):
  728         # Setup a pocket surface group and add it to chain pocket group
  729         SurfaceGroupName = "%s.Surface" % (ChainPocketGroupName)
  730         PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketSurfaceGroup"] = SurfaceGroupName
  731         PyMOLObjectNames["Pockets"][ChainID][PocketID]["ChainPocketGroupMembers"].append(SurfaceGroupName)
  732 
  733         PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketSurfaceGroupMembers"] = []
  734 
  735         # Setup a generic color surface...
  736         Name = "%s.Surface" % (SurfaceGroupName)
  737         PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketSurface"] = Name
  738         PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketSurfaceGroupMembers"].append(Name)
  739 
  740         if GetPocketSurfaceChainStatus(FileIndex, ChainID, PocketID):
  741             # Surface colored by hydrophobicity...
  742             Name = "%s.Hydrophobicity" % (SurfaceGroupName)
  743             PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketHydrophobicitySurface"] = Name
  744             PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketSurfaceGroupMembers"].append(Name)
  745 
  746             # Surface colored by hydrophobicity and charge...
  747             Name = "%s.Hydrophobicity_Charge" % (SurfaceGroupName)
  748             PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketHydrophobicityChargeSurface"] = Name
  749             PyMOLObjectNames["Pockets"][ChainID][PocketID]["PocketSurfaceGroupMembers"].append(Name)
  750 
  751 
  752 def SetupChainPocketGroupName(FileIndex, ChainGroupName, ChainID, PocketID):
  753     """Setup pocket group PyMOL object name."""
  754 
  755     ChainPocketGroupName = "%s.Fpocket%s" % (ChainGroupName, PocketID)
  756 
  757     if not OptionsInfo["FpocketPropertiesAppend"]:
  758         return ChainPocketGroupName
  759 
  760     PocketPropertiesName = SetupFpocketPropertiesForGroupName(FileIndex, ChainGroupName, ChainID, PocketID)
  761 
  762     ChainPocketGroupName = "%s_%s" % (ChainPocketGroupName, PocketPropertiesName)
  763 
  764     return ChainPocketGroupName
  765 
  766 
  767 def SetupFpocketPropertiesForGroupName(FileIndex, ChainGroupName, ChainID, PocketID):
  768     """Setup fpocket properties for PyMOL group name."""
  769 
  770     ChainsAndPocketsInfo = OptionsInfo["FpocketInfilesInfo"]["ChainsAndPocketsInfo"][FileIndex]
  771 
  772     PocketScore = FormatFpocketPropertyForGroupName(ChainsAndPocketsInfo["PocketScore"][ChainID][PocketID])
  773     DrugScore = FormatFpocketPropertyForGroupName(ChainsAndPocketsInfo["DrugScore"][ChainID][PocketID])
  774     HydrophobicityScore = FormatFpocketPropertyForGroupName(
  775         ChainsAndPocketsInfo["HydrophobicityScore"][ChainID][PocketID]
  776     )
  777     PolarityScore = FormatFpocketPropertyForGroupName(ChainsAndPocketsInfo["PolarityScore"][ChainID][PocketID])
  778     PocketVolume = FormatFpocketPropertyForGroupName(ChainsAndPocketsInfo["PocketVolume"][ChainID][PocketID])
  779 
  780     PocketProperties = "S%s_D%s_V%s_H%s_P%s" % (
  781         PocketScore,
  782         DrugScore,
  783         PocketVolume,
  784         HydrophobicityScore,
  785         PolarityScore,
  786     )
  787 
  788     return PocketProperties
  789 
  790 
  791 def FormatFpocketPropertyForGroupName(PocketProperty):
  792     """Format fpocket property for PyMOL group name."""
  793 
  794     if PocketProperty is None:
  795         PocketProperty = "NA"
  796     else:
  797         PocketProperty = "%.2f" % float(PocketProperty)
  798         PocketProperty = re.sub(r"\.", "p", PocketProperty)
  799         PocketProperty = re.sub("^-", "neg", PocketProperty)
  800 
  801     return PocketProperty
  802 
  803 
  804 def GetChainAloneContainsSurfacesStatus(FileIndex, ChainID):
  805     """Get status of surfaces present in chain alone object."""
  806 
  807     # Always set up generic color surfaces...
  808     return True
  809 
  810 
  811 def GetChainAloneSurfaceChainStatus(FileIndex, ChainID):
  812     """Get status of  surfaces for chain alone object."""
  813 
  814     return OptionsInfo["SurfaceChain"]
  815 
  816 
  817 def GetPocketContainsSurfaceStatus(FileIndex, ChainID, PocketID):
  818     """Get status of surfaces present in  a pocket object."""
  819 
  820     # Always set up generic color surfaces...
  821     return True
  822 
  823 
  824 def GetPocketSurfaceChainStatus(FileIndex, ChainID, PocketID):
  825     """Get status of  surfaces for pocket object."""
  826 
  827     return OptionsInfo["PocketSurface"]
  828 
  829 
  830 def CopyPDBFilesForPML():
  831     """Copy appropriate PDB files for PyMOL to OutfilesDir"""
  832 
  833     OutfilesDir = OptionsInfo["OutfilesDir"]
  834 
  835     MiscUtil.PrintInfo("\nCopying appropriate PDB files to directory %s..." % OutfilesDir)
  836 
  837     # Copy input PDB files...
  838     for FileIndex in range(0, len(OptionsInfo["InfilesInfo"]["InfilesNames"])):
  839         Infile = OptionsInfo["InfilesInfo"]["InfilesNames"][FileIndex]
  840         NewInfilePath = os.path.join(OutfilesDir, Infile)
  841 
  842         shutil.copyfile(Infile, NewInfilePath)
  843 
  844     # Copy fpocket output PDB files...
  845     for FileIndex in range(0, len(OptionsInfo["FpocketInfilesInfo"]["InfilesNames"])):
  846         Infile = OptionsInfo["FpocketInfilesInfo"]["InfilesNames"][FileIndex]
  847         InfilePath = OptionsInfo["FpocketInfilesInfo"]["InfilesPaths"][FileIndex]
  848         NewInfilePath = os.path.join(OutfilesDir, Infile)
  849 
  850         shutil.copyfile(InfilePath, NewInfilePath)
  851 
  852 
  853 def RetrieveInfilesInfo():
  854     """Retrieve information for input files."""
  855 
  856     InfilesInfo = {}
  857 
  858     InfilesInfo["InfilesNames"] = []
  859     InfilesInfo["InfilesRoots"] = []
  860     InfilesInfo["ChainsAndLigandsInfo"] = []
  861 
  862     for Infile in OptionsInfo["InfilesNames"]:
  863         FileDir, FileName, FileExt = MiscUtil.ParseFileName(Infile)
  864         InfileRoot = FileName
  865 
  866         ChainsAndLigandInfo = PyMOLUtil.GetChainsAndLigandsInfo(Infile, InfileRoot)
  867 
  868         InfilesInfo["InfilesNames"].append(Infile)
  869         InfilesInfo["InfilesRoots"].append(InfileRoot)
  870         InfilesInfo["ChainsAndLigandsInfo"].append(ChainsAndLigandInfo)
  871 
  872     OptionsInfo["InfilesInfo"] = InfilesInfo
  873 
  874 
  875 def RetrieveRefFileInfo():
  876     """Retrieve information for ref file."""
  877 
  878     RefFileInfo = {}
  879     if not OptionsInfo["Align"]:
  880         OptionsInfo["RefFileInfo"] = RefFileInfo
  881         return
  882 
  883     RefFile = OptionsInfo["RefFileName"]
  884 
  885     FileDir, FileName, FileExt = MiscUtil.ParseFileName(RefFile)
  886     RefFileRoot = FileName
  887 
  888     if re.match("^FirstInputFile$", OptionsInfo["AlignRefFile"], re.I):
  889         ChainsAndLigandInfo = OptionsInfo["InfilesInfo"]["ChainsAndLigandsInfo"][0]
  890     else:
  891         MiscUtil.PrintInfo("\nRetrieving chain and ligand information for alignment reference file %s..." % RefFile)
  892         ChainsAndLigandInfo = PyMOLUtil.GetChainsAndLigandsInfo(RefFile, RefFileRoot)
  893 
  894     RefFileInfo["RefFileName"] = RefFile
  895     RefFileInfo["RefFileRoot"] = RefFileRoot
  896     RefFileInfo["PyMOLObjectName"] = "AlignRef_%s" % RefFileRoot
  897     RefFileInfo["ChainsAndLigandsInfo"] = ChainsAndLigandInfo
  898 
  899     OptionsInfo["RefFileInfo"] = RefFileInfo
  900 
  901 
  902 def RetrieveFpocketResultFilesInfo():
  903     """Check and retrieve information for Fpocket result files."""
  904 
  905     FpocketInfilesInfo = {}
  906 
  907     FpocketInfilesInfo["InfilesNames"] = []
  908     FpocketInfilesInfo["InfilesRoots"] = []
  909     FpocketInfilesInfo["InfilesPaths"] = []
  910     FpocketInfilesInfo["InfilesDirs"] = []
  911 
  912     FpocketInfilesInfo["InfilesPocketsDirs"] = []
  913     FpocketInfilesInfo["InfilesPocketsPDBFilesCount"] = []
  914 
  915     FpocketInfilesInfo["ChainsAndPocketsInfo"] = []
  916 
  917     for Infile in OptionsInfo["InfilesNames"]:
  918         MiscUtil.PrintInfo("\nRetrieving Fpocket result file information for input file %s..." % Infile)
  919 
  920         FileDir, FileName, FileExt = MiscUtil.ParseFileName(Infile)
  921         InfileRoot = FileName
  922 
  923         FpocketInfileDir = "%s_out" % InfileRoot
  924         FpocketInfileRoot = "%s_out" % InfileRoot
  925         FpocketInfileName = "%s.pdb" % FpocketInfileRoot
  926         FpocketInfilePath = os.path.join(FpocketInfileDir, FpocketInfileName)
  927 
  928         FpocketInfilePocketsDir = os.path.join(FpocketInfileDir, "pockets")
  929 
  930         if not os.path.isdir(FpocketInfileDir):
  931             MiscUtil.PrintError(
  932                 "Fpocket result file directory, %s, is missing for input PDB file, %s." % (FpocketInfileDir, Infile)
  933             )
  934 
  935         if not os.path.isfile(FpocketInfilePath):
  936             MiscUtil.PrintError(
  937                 "Fpocket result PDB file, %s, is missing for input PDB file, %s." % (FpocketInfilePath, Infile)
  938             )
  939 
  940         if not os.path.isdir(FpocketInfilePocketsDir):
  941             MiscUtil.PrintError(
  942                 "Fpocket pockets result file directory, %s, is missing for input PDB file, %s."
  943                 % (FpocketInfilePocketsDir, Infile)
  944             )
  945 
  946         PocketsPDBFiles = glob.glob(os.path.join(FpocketInfilePocketsDir, "pocket*_atm.pdb"))
  947         PocketsPDBFilesCount = len(PocketsPDBFiles)
  948         if PocketsPDBFilesCount == 0:
  949             MiscUtil.PrintError(
  950                 "Fpocket pockets result files missing in directory, %s, for input PDB file, %s."
  951                 % (FpocketInfilePocketsDir, Infile)
  952             )
  953 
  954         # Retrieve chains and pockets...
  955         ChainsAndPocketsInfo = GetFpocketChainsAndPocketsInfo(
  956             FpocketInfileRoot, FpocketInfilePath, FpocketInfileDir, FpocketInfilePocketsDir
  957         )
  958 
  959         FpocketInfilesInfo["InfilesNames"].append(FpocketInfileName)
  960         FpocketInfilesInfo["InfilesRoots"].append(FpocketInfileRoot)
  961         FpocketInfilesInfo["InfilesPaths"].append(FpocketInfilePath)
  962         FpocketInfilesInfo["InfilesDirs"].append(FpocketInfileDir)
  963 
  964         FpocketInfilesInfo["InfilesPocketsDirs"].append(FpocketInfilePocketsDir)
  965         FpocketInfilesInfo["InfilesPocketsPDBFilesCount"].append(PocketsPDBFilesCount)
  966 
  967         FpocketInfilesInfo["ChainsAndPocketsInfo"].append(ChainsAndPocketsInfo)
  968 
  969         MiscUtil.PrintInfo("PDB result file: %s..." % FpocketInfileName)
  970         MiscUtil.PrintInfo("Total number of pockets: %s..." % ChainsAndPocketsInfo["NumOfPockets"])
  971 
  972         MiscUtil.PrintInfo("Pocket Chain IDs: %s" % " ".join(ChainsAndPocketsInfo["ChainIDs"]))
  973         for ChainID in ChainsAndPocketsInfo["ChainIDs"]:
  974             MiscUtil.PrintInfo(
  975                 "Pocket Chain ID: %s; NumOfPockets: %s; PocketIDs: %s"
  976                 % (
  977                     ChainID,
  978                     len(ChainsAndPocketsInfo["PocketIDs"][ChainID]),
  979                     " ".join(ChainsAndPocketsInfo["PocketIDs"][ChainID]),
  980                 )
  981             )
  982 
  983     OptionsInfo["FpocketInfilesInfo"] = FpocketInfilesInfo
  984 
  985 
  986 def GetFpocketChainsAndPocketsInfo(FpocketInfileRoot, FpocketInfilePath, FpocketInfileDir, FpocketInfilePocketsDir):
  987     """Get chains and pockets information for Fpocket result files."""
  988 
  989     ChainsAndPocketsInfo = {}
  990     ChainsAndPocketsInfo["ChainIDs"] = []
  991     ChainsAndPocketsInfo["PocketIDs"] = {}
  992     ChainsAndPocketsInfo["PocketResNums"] = {}
  993 
  994     ChainsAndPocketsInfo["DrugScore"] = {}
  995     ChainsAndPocketsInfo["PocketScore"] = {}
  996     ChainsAndPocketsInfo["HydrophobicityScore"] = {}
  997     ChainsAndPocketsInfo["PolarityScore"] = {}
  998     ChainsAndPocketsInfo["PocketVolume"] = {}
  999 
 1000     ChainsAndPocketsInfo["NumOfPockets"] = 0
 1001 
 1002     # Retrieve number of pockets and pocket IDs...
 1003     PocketIDs = GetPocketIDs(FpocketInfilePath, FpocketInfileRoot)
 1004     ChainsAndPocketsInfo["NumOfPockets"] = len(PocketIDs)
 1005 
 1006     # Retrieve residue numbers for fpockets across the chains...
 1007     for PocketID in PocketIDs:
 1008         PocketAtomFile = os.path.join(FpocketInfilePocketsDir, "pocket%s_atm.pdb" % PocketID)
 1009 
 1010         if not os.path.isfile(PocketAtomFile):
 1011             MiscUtil.PrintError(
 1012                 "Fpocket result PDB file, %s, is missing for pocket ID, %s." % (PocketAtomFile, PocketID)
 1013             )
 1014 
 1015         # Retrieve pocket properites for a pocket across chains...
 1016         PocketScore, DrugScore, HydrophobicityScore, PolarityScore, PocketVolume = GetPocketProperties(PocketAtomFile)
 1017 
 1018         MolName = "pocket%s_atm" % PocketID
 1019         pymol.cmd.load(PocketAtomFile, MolName)
 1020         SelectionCmd = "(%s)" % MolName
 1021 
 1022         pymol.stored.FpocketInfo = []
 1023         pymol.cmd.iterate(SelectionCmd, "pymol.stored.FpocketInfo.append([chain, resi, resn])")
 1024         pymol.cmd.delete(MolName)
 1025 
 1026         for ChainID, ResNum, ResName in pymol.stored.FpocketInfo:
 1027             if ChainID not in ChainsAndPocketsInfo["ChainIDs"]:
 1028                 ChainsAndPocketsInfo["PocketIDs"][ChainID] = []
 1029                 ChainsAndPocketsInfo["PocketResNums"][ChainID] = {}
 1030 
 1031                 ChainsAndPocketsInfo["DrugScore"][ChainID] = {}
 1032                 ChainsAndPocketsInfo["PocketScore"][ChainID] = {}
 1033                 ChainsAndPocketsInfo["HydrophobicityScore"][ChainID] = {}
 1034                 ChainsAndPocketsInfo["PolarityScore"][ChainID] = {}
 1035                 ChainsAndPocketsInfo["PocketVolume"][ChainID] = {}
 1036 
 1037                 ChainsAndPocketsInfo["ChainIDs"].append(ChainID)
 1038 
 1039             if PocketID not in ChainsAndPocketsInfo["PocketIDs"][ChainID]:
 1040                 ChainsAndPocketsInfo["PocketResNums"][ChainID][PocketID] = []
 1041                 ChainsAndPocketsInfo["PocketIDs"][ChainID].append(PocketID)
 1042 
 1043                 # Track pocket and drug score across chains...
 1044                 ChainsAndPocketsInfo["DrugScore"][ChainID][PocketID] = DrugScore
 1045                 ChainsAndPocketsInfo["PocketScore"][ChainID][PocketID] = PocketScore
 1046                 ChainsAndPocketsInfo["HydrophobicityScore"][ChainID][PocketID] = HydrophobicityScore
 1047                 ChainsAndPocketsInfo["PolarityScore"][ChainID][PocketID] = PolarityScore
 1048                 ChainsAndPocketsInfo["PocketVolume"][ChainID][PocketID] = PocketVolume
 1049 
 1050             if ResNum not in ChainsAndPocketsInfo["PocketResNums"][ChainID][PocketID]:
 1051                 ChainsAndPocketsInfo["PocketResNums"][ChainID][PocketID].append(ResNum)
 1052 
 1053     ChainsAndPocketsInfo["ChainIDs"] = sorted(ChainsAndPocketsInfo["ChainIDs"])
 1054 
 1055     return ChainsAndPocketsInfo
 1056 
 1057 
 1058 def GetPolymerChainIDs(Infile, MolName):
 1059     """Get chain IDs for main chain excluding hetero atoms."""
 1060 
 1061     pymol.cmd.load(Infile, MolName)
 1062     ChainIDs = PyMOLUtil.GetChains(MolName)
 1063 
 1064     # Retrieve polymer chains...
 1065     SelectedChainIDs = []
 1066     for ChainID in ChainIDs:
 1067         AtomsCount = pymol.cmd.count_atoms("(%s and (chain %s) and (polymer) and (not hetatm))" % (MolName, ChainID))
 1068         if AtomsCount > 0:
 1069             SelectedChainIDs.append(ChainID)
 1070 
 1071     pymol.cmd.delete(MolName)
 1072 
 1073     return SelectedChainIDs
 1074 
 1075 
 1076 def GetPocketIDs(Infile, MolName):
 1077     """Get pocket IDs."""
 1078 
 1079     # Fpockets are annonated in PDB file as STP residue name and unique residue
 1080     # numbers...
 1081     pymol.cmd.load(Infile, MolName)
 1082     SelectionCmd = "(%s and (resn STP) and hetatm)" % MolName
 1083 
 1084     pymol.stored.FpocketIDsInfo = []
 1085     pymol.cmd.iterate(SelectionCmd, "pymol.stored.FpocketIDsInfo.append(resi)")
 1086     pymol.cmd.delete(MolName)
 1087 
 1088     PocketIDs = []
 1089     for PocketID in pymol.stored.FpocketIDsInfo:
 1090         if PocketID not in PocketIDs:
 1091             PocketIDs.append(PocketID)
 1092 
 1093     return PocketIDs
 1094 
 1095 
 1096 def ProcessChainAndPocketIDs():
 1097     """Process specified chain and pocket  IDs for infiles."""
 1098 
 1099     OptionsInfo["FpocketInfilesInfo"]["SpecifiedChainsAndPocketsInfo"] = []
 1100 
 1101     for FileIndex in range(0, len(OptionsInfo["InfilesInfo"]["InfilesNames"])):
 1102         MiscUtil.PrintInfo(
 1103             "\nProcessing specified chain and Pocket IDs for input file %s..."
 1104             % OptionsInfo["FpocketInfilesInfo"]["InfilesNames"][FileIndex]
 1105         )
 1106 
 1107         ChainsAndPocketsInfo = OptionsInfo["FpocketInfilesInfo"]["ChainsAndPocketsInfo"][FileIndex]
 1108 
 1109         SpecifiedChainsAndPocketsInfo = ProcessChainsAndPocketsOptionsInfo(ChainsAndPocketsInfo)
 1110         OptionsInfo["FpocketInfilesInfo"]["SpecifiedChainsAndPocketsInfo"].append(SpecifiedChainsAndPocketsInfo)
 1111 
 1112         CheckPresenceOfValidPocketIDs(ChainsAndPocketsInfo, SpecifiedChainsAndPocketsInfo)
 1113 
 1114 
 1115 def GetPocketProperties(PocketAtomFile):
 1116     """Get pocket properites from the pocket PDB file."""
 1117 
 1118     PocketScore, DrugScore, HydrophobicityScore, PolarityScore, PocketVolume = [None] * 5
 1119 
 1120     PocketAtomFH = open(PocketAtomFile, "r")
 1121     if PocketAtomFH is None:
 1122         return (PocketScore, DrugScore, HydrophobicityScore, PolarityScore, PocketVolume)
 1123 
 1124     for Line in PocketAtomFH:
 1125         Line = Line.rstrip()
 1126         if re.search("Pocket Score", Line, re.I):
 1127             LineWords = Line.split()
 1128             PocketScore = LineWords[-1]
 1129         elif re.search("Drug Score", Line, re.I):
 1130             LineWords = Line.split()
 1131             DrugScore = LineWords[-1]
 1132         elif re.search("Hydrophobicity Score", Line, re.I):
 1133             LineWords = Line.split()
 1134             HydrophobicityScore = LineWords[-1]
 1135         elif re.search("Polarity Score", Line, re.I):
 1136             LineWords = Line.split()
 1137             PolarityScore = LineWords[-1]
 1138         elif re.search(r"Pocket volume \(Monte Carlo\)", Line, re.I):
 1139             LineWords = Line.split()
 1140             PocketVolume = LineWords[-1]
 1141 
 1142         if not re.match("^HEADER", Line, re.I):
 1143             break
 1144 
 1145     PocketAtomFH.close()
 1146 
 1147     return (PocketScore, DrugScore, HydrophobicityScore, PolarityScore, PocketVolume)
 1148 
 1149 
 1150 def ProcessChainsAndPocketsOptionsInfo(ChainsAndPocketsInfo):
 1151     """Process specified chain and pocket IDs using command line options."""
 1152 
 1153     SpecifiedChainsAndPocketsInfo = {}
 1154     SpecifiedChainsAndPocketsInfo["ChainIDs"] = []
 1155     SpecifiedChainsAndPocketsInfo["PocketIDs"] = {}
 1156 
 1157     ProcessChainsOptionInfo(ChainsAndPocketsInfo, SpecifiedChainsAndPocketsInfo)
 1158     ProcessPocketsOptionInfo(ChainsAndPocketsInfo, SpecifiedChainsAndPocketsInfo)
 1159 
 1160     return SpecifiedChainsAndPocketsInfo
 1161 
 1162 
 1163 def ProcessChainsOptionInfo(ChainsAndPocketsInfo, SpecifiedChainsAndPocketsInfo):
 1164     """Process chain IDs"""
 1165 
 1166     MiscUtil.PrintInfo("Processing chain IDs...")
 1167 
 1168     ChainsOptionName = "-c, --chainIDs"
 1169     ChainsOptionValue = OptionsInfo["ChainIDs"]
 1170 
 1171     if re.match("^All$", ChainsOptionValue, re.I):
 1172         SpecifiedChainsAndPocketsInfo["ChainIDs"] = ChainsAndPocketsInfo["ChainIDs"]
 1173         return
 1174     elif re.match("^(First|Auto)$", ChainsOptionValue, re.I):
 1175         FirstChainID = ChainsAndPocketsInfo["ChainIDs"][0] if (len(ChainsAndPocketsInfo["ChainIDs"])) else None
 1176         if FirstChainID is not None:
 1177             SpecifiedChainsAndPocketsInfo["ChainIDs"].append(FirstChainID)
 1178         return
 1179 
 1180     ChainIDs = re.sub(" ", "", ChainsOptionValue)
 1181     if not ChainIDs:
 1182         MiscUtil.PrintError('No valid value specified using "%s" option.' % ChainsOptionName)
 1183 
 1184     ChainIDsList = ChainsAndPocketsInfo["ChainIDs"]
 1185     SpecifiedChainIDsList = []
 1186 
 1187     ChainIDsWords = ChainIDs.split(",")
 1188     for ChainID in ChainIDsWords:
 1189         if ChainID not in ChainIDsList:
 1190             MiscUtil.PrintWarning(
 1191                 'The chain ID, %s, specified using "%s" option is not valid. It\'ll be ignored. Valid chain IDs: %s'
 1192                 % (ChainID, ChainsOptionName, ", ".join(ChainIDsList))
 1193             )
 1194             continue
 1195         if ChainID in SpecifiedChainIDsList:
 1196             MiscUtil.PrintWarning(
 1197                 'The chain ID, %s, has already been specified using "%s" option. It\'ll be ignored.'
 1198                 % (ChainID, ChainsOptionName)
 1199             )
 1200             continue
 1201         SpecifiedChainIDsList.append(ChainID)
 1202 
 1203     if not len(SpecifiedChainIDsList):
 1204         MiscUtil.PrintError(
 1205             'No valid chain IDs "%s"  specified using "%s" option.' % (ChainsOptionValue, ChainsOptionName)
 1206         )
 1207 
 1208     SpecifiedChainsAndPocketsInfo["ChainIDs"] = SpecifiedChainIDsList
 1209 
 1210 
 1211 def ProcessPocketsOptionInfo(ChainsAndPocketsInfo, SpecifiedChainsAndPocketsInfo):
 1212     """Process pocket IDs"""
 1213 
 1214     MiscUtil.PrintInfo("Processing pocket IDs...")
 1215 
 1216     PocketsModeOptionValue = OptionsInfo["FpocketMode"]
 1217 
 1218     PocketsIDsOptionName = "--fpocketIDs"
 1219     PocketsIDsOptionValue = OptionsInfo["FpocketIDs"]
 1220 
 1221     # Intialize pocketIDs...
 1222     for ChainID in SpecifiedChainsAndPocketsInfo["ChainIDs"]:
 1223         SpecifiedChainsAndPocketsInfo["PocketIDs"][ChainID] = []
 1224 
 1225     if re.match("^All$", PocketsModeOptionValue, re.I):
 1226         for ChainID in SpecifiedChainsAndPocketsInfo["ChainIDs"]:
 1227             SpecifiedChainsAndPocketsInfo["PocketIDs"][ChainID] = ChainsAndPocketsInfo["PocketIDs"][ChainID]
 1228         return
 1229     elif re.match("^TopN$", PocketsModeOptionValue, re.I):
 1230         # Setup TopN pocket IDs for each chain...
 1231         TopNPocketsCount = int(PocketsIDsOptionValue[0])
 1232         for ChainID in SpecifiedChainsAndPocketsInfo["ChainIDs"]:
 1233             TopNPocketsIDs = []
 1234             NumOfPockets = len(ChainsAndPocketsInfo["PocketIDs"][ChainID])
 1235 
 1236             if NumOfPockets:
 1237                 if TopNPocketsCount > NumOfPockets:
 1238                     TopNPocketsIDs = ChainsAndPocketsInfo["PocketIDs"][ChainID]
 1239                 else:
 1240                     TopNPocketsIDs = ChainsAndPocketsInfo["PocketIDs"][ChainID][0:TopNPocketsCount]
 1241 
 1242             SpecifiedChainsAndPocketsInfo["PocketIDs"][ChainID] = TopNPocketsIDs
 1243         return
 1244 
 1245     # Process explicitly specified pocket IDs...
 1246     PocketIDsWords = PocketsIDsOptionValue
 1247     if not len(PocketIDsWords):
 1248         MiscUtil.PrintError('No valid value specified using "%s" option.' % PocketsIDsOptionName)
 1249 
 1250     for ChainID in SpecifiedChainsAndPocketsInfo["ChainIDs"]:
 1251         PocketIDsList = ChainsAndPocketsInfo["PocketIDs"][ChainID]
 1252         SpecifiedPocketIDsList = []
 1253 
 1254         for PocketID in PocketIDsWords:
 1255             if PocketID not in PocketIDsList:
 1256                 MiscUtil.PrintWarning(
 1257                     'The pocket ID, %s, specified using "%s" option is not valid for chain, %s. It\'ll be ignored. Valid pocket IDs are listed earlier.'
 1258                     % (PocketID, PocketsIDsOptionName, ChainID)
 1259                 )
 1260                 continue
 1261 
 1262             if PocketID in SpecifiedPocketIDsList:
 1263                 MiscUtil.PrintWarning(
 1264                     'The pocket ID, %s, has already been specified using "%s" option. It\'ll be ignored.'
 1265                     % (PocketID, PocketsIDsOptionName)
 1266                 )
 1267                 continue
 1268 
 1269             SpecifiedPocketIDsList.append(PocketID)
 1270 
 1271         if not len(SpecifiedPocketIDsList):
 1272             MiscUtil.PrintWarning(
 1273                 'No valid pocket IDs "%s" specified using "%s" option for chain ID, %s.'
 1274                 % (PocketsIDsOptionValue, PocketsIDsOptionName, ChainID)
 1275             )
 1276 
 1277         SpecifiedChainsAndPocketsInfo["PocketIDs"][ChainID] = SpecifiedPocketIDsList
 1278 
 1279 
 1280 def CheckPresenceOfValidPocketIDs(ChainsAndPocketsInfo, SpecifiedChainsAndPocketsInfo):
 1281     """Check presence of valid pocket IDs."""
 1282 
 1283     MiscUtil.PrintInfo("\nSpecified chain IDs: %s" % (", ".join(SpecifiedChainsAndPocketsInfo["ChainIDs"])))
 1284 
 1285     for ChainID in SpecifiedChainsAndPocketsInfo["ChainIDs"]:
 1286         if len(SpecifiedChainsAndPocketsInfo["PocketIDs"][ChainID]):
 1287             MiscUtil.PrintInfo(
 1288                 "Chain ID: %s; Specified PocketIDs: %s"
 1289                 % (ChainID, ", ".join(SpecifiedChainsAndPocketsInfo["PocketIDs"][ChainID]))
 1290             )
 1291         else:
 1292             MiscUtil.PrintInfo("Chain IDs: %s; Specified PocketIDs: None" % (ChainID))
 1293             MiscUtil.PrintWarning(
 1294                 "No valid pocket IDs found for chain ID, %s. PyMOL groups and objects related to fpockets won't be created."
 1295                 % (ChainID)
 1296             )
 1297 
 1298 
 1299 def RetrieveFirstChainID(FileIndex, FpocketComplexMode):
 1300     """Get first chain ID."""
 1301 
 1302     FirstChainID = None
 1303     if FpocketComplexMode:
 1304         ChainsAndPocketsInfo = OptionsInfo["FpocketInfilesInfo"]["ChainsAndPocketsInfo"][FileIndex]
 1305         if len(ChainsAndPocketsInfo["ChainIDs"]):
 1306             FirstChainID = ChainsAndPocketsInfo["ChainIDs"][0]
 1307     else:
 1308         ChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["ChainsAndLigandsInfo"][FileIndex]
 1309         if len(ChainsAndLigandsInfo["ChainIDs"]):
 1310             FirstChainID = ChainsAndLigandsInfo["ChainIDs"][0]
 1311 
 1312     return FirstChainID
 1313 
 1314 
 1315 def ProcessSurfaceAtomTypesColors():
 1316     """Process surface atom types colors."""
 1317 
 1318     AtomTypesColorNamesInfo = PyMOLUtil.ProcessSurfaceAtomTypesColorsOptionsInfo(
 1319         "--surfaceAtomTypesColors", OptionsInfo["SurfaceAtomTypesColors"]
 1320     )
 1321     OptionsInfo["AtomTypesColorNames"] = AtomTypesColorNamesInfo
 1322 
 1323 
 1324 def CheckAndSetupOutfilesDir():
 1325     """Check and setup a directory for output files used by PyMOL."""
 1326 
 1327     Outfile = Options["--outfile"]
 1328     OutfilesDir = Options["--outfilesDir"]
 1329 
 1330     FileDir, FileName, FileExt = MiscUtil.ParseFileName(Outfile)
 1331     if re.match("^auto$", OutfilesDir, re.I):
 1332         OutfilesDir = "%s_out_PyMOL" % FileName
 1333 
 1334     if os.path.isdir(OutfilesDir):
 1335         if not OptionsInfo["Overwrite"]:
 1336             MiscUtil.PrintError(
 1337                 'The output directory, %s, already exists. Use option "--ov" or "--overwrite" and try again.'
 1338                 % OutfilesDir
 1339             )
 1340         MiscUtil.PrintInfo("Using existing outout dir %s..." % OutfilesDir)
 1341     else:
 1342         MiscUtil.PrintInfo("Creating new output dir %s..." % OutfilesDir)
 1343         os.mkdir(OutfilesDir)
 1344 
 1345     OptionsInfo["Outfile"] = Outfile
 1346     OptionsInfo["OutfilesDir"] = OutfilesDir
 1347 
 1348     OptionsInfo["PMLOutfile"] = Outfile
 1349     OptionsInfo["PMLOutfilePath"] = os.path.join(OutfilesDir, Outfile)
 1350 
 1351 
 1352 def ProcessOptions():
 1353     """Process and validate command line arguments and options"""
 1354 
 1355     MiscUtil.PrintInfo("Processing options...")
 1356 
 1357     # Validate options...
 1358     ValidateOptions()
 1359 
 1360     OptionsInfo["Align"] = True if re.match("^Yes$", Options["--align"], re.I) else False
 1361     OptionsInfo["AlignMethod"] = Options["--alignMethod"].lower()
 1362     OptionsInfo["AlignMode"] = Options["--alignMode"]
 1363 
 1364     OptionsInfo["FpocketPropertiesAppend"] = (
 1365         True if re.match("^Yes$", Options["--fpocketPropertiesAppend"], re.I) else False
 1366     )
 1367 
 1368     OptionsInfo["Infiles"] = Options["--infiles"]
 1369     OptionsInfo["InfilesNames"] = Options["--infileNames"]
 1370 
 1371     OptionsInfo["AlignRefFile"] = Options["--alignRefFile"]
 1372     if re.match("^FirstInputFile$", Options["--alignRefFile"], re.I):
 1373         OptionsInfo["RefFileName"] = OptionsInfo["InfilesNames"][0]
 1374     else:
 1375         OptionsInfo["RefFileName"] = Options["--alignRefFile"]
 1376 
 1377     OptionsInfo["ChainIDs"] = Options["--chainIDs"]
 1378 
 1379     OptionsInfo["FpocketMode"] = Options["--fpocketMode"]
 1380     OptionsInfo["FpocketIDs"] = Options["--fpocketIDsList"]
 1381 
 1382     OptionsInfo["Overwrite"] = Options["--overwrite"]
 1383 
 1384     OptionsInfo["LabelFontID"] = int(Options["--labelFontID"])
 1385 
 1386     OptionsInfo["PocketColorByPocketNum"] = (
 1387         True if re.match("^Yes$", Options["--pocketColorByPocketNum"], re.I) else False
 1388     )
 1389     OptionsInfo["PocketLabel"] = True if re.match("^Yes$", Options["--pocketLabel"], re.I) else False
 1390     OptionsInfo["PocketLabelType"] = Options["--pocketLabelType"]
 1391     OptionsInfo["ThreeLetterPocketLabelType"] = (
 1392         True if re.match("^ThreeLetter$", Options["--pocketLabelType"], re.I) else False
 1393     )
 1394 
 1395     OptionsInfo["PocketNumOneColor"] = "gray80"
 1396 
 1397     OptionsInfo["PocketSurface"] = True if re.match("^Yes$", Options["--pocketSurface"], re.I) else False
 1398     OptionsInfo["SurfaceChain"] = True if re.match("^Yes$", Options["--surfaceChain"], re.I) else False
 1399 
 1400     OptionsInfo["SurfaceColor"] = Options["--surfaceColor"]
 1401     OptionsInfo["SurfaceColorPalette"] = Options["--surfaceColorPalette"]
 1402     OptionsInfo["SurfaceAtomTypesColors"] = Options["--surfaceAtomTypesColors"]
 1403     ProcessSurfaceAtomTypesColors()
 1404 
 1405     OptionsInfo["SphereScale"] = float(Options["--sphereScale"])
 1406     OptionsInfo["SphereTransparency"] = float(Options["--sphereTransparency"])
 1407 
 1408     OptionsInfo["SurfaceTransparency"] = float(Options["--surfaceTransparency"])
 1409 
 1410     # Check and setup outfile dir before processing input files...
 1411     CheckAndSetupOutfilesDir()
 1412 
 1413     RetrieveInfilesInfo()
 1414     RetrieveRefFileInfo()
 1415     RetrieveFpocketResultFilesInfo()
 1416 
 1417     # Process specified chain and pocket IDs..
 1418     ProcessChainAndPocketIDs()
 1419 
 1420 
 1421 def RetrieveOptions():
 1422     """Retrieve command line arguments and options"""
 1423 
 1424     # Get options...
 1425     global Options
 1426     Options = docopt(_docoptUsage_)
 1427 
 1428     # Set current working directory to the specified directory...
 1429     WorkingDir = Options["--workingdir"]
 1430     if WorkingDir:
 1431         os.chdir(WorkingDir)
 1432 
 1433     # Handle examples option...
 1434     if "--examples" in Options and Options["--examples"]:
 1435         MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(__doc__))
 1436         sys.exit(0)
 1437 
 1438 
 1439 def ValidateOptions():
 1440     """Validate option values"""
 1441 
 1442     MiscUtil.ValidateOptionTextValue("--align", Options["--align"], "yes no")
 1443     MiscUtil.ValidateOptionTextValue("--alignMethod", Options["--alignMethod"], "align cealign super")
 1444     MiscUtil.ValidateOptionTextValue("--alignMode", Options["--alignMode"], "FirstChain Complex")
 1445 
 1446     MiscUtil.ValidateOptionTextValue("--fpocketPropertiesAppend", Options["--fpocketPropertiesAppend"], "yes no")
 1447 
 1448     # Expand infiles to handle presence of multiple input files...
 1449     InfileNames = MiscUtil.ExpandFileNames(Options["--infiles"], ",")
 1450     if not len(InfileNames):
 1451         MiscUtil.PrintError('No input files specified for "-i, --infiles" option')
 1452 
 1453     # Validate file extensions...
 1454     for Infile in InfileNames:
 1455         MiscUtil.ValidateOptionFilePath("-i, --infiles", Infile)
 1456         MiscUtil.ValidateOptionFileExt("-i, --infiles", Infile, "pdb")
 1457         MiscUtil.ValidateOptionsDistinctFileNames("-i, --infiles", Infile, "-o, --outfile", Options["--outfile"])
 1458     Options["--infileNames"] = InfileNames
 1459 
 1460     MiscUtil.ValidateOptionFileExt("-o, --outfile", Options["--outfile"], "pml")
 1461 
 1462     if re.match("^yes$", Options["--align"], re.I):
 1463         if not re.match("^FirstInputFile$", Options["--alignRefFile"], re.I):
 1464             AlignRefFile = Options["--alignRefFile"]
 1465             MiscUtil.ValidateOptionFilePath("--alignRefFile", AlignRefFile)
 1466             MiscUtil.ValidateOptionFileExt("--alignRefFile", AlignRefFile, "pdb")
 1467             MiscUtil.ValidateOptionsDistinctFileNames(
 1468                 "--AlignRefFile", AlignRefFile, "-o, --outfile", Options["--outfile"]
 1469             )
 1470 
 1471     MiscUtil.ValidateOptionTextValue("--fpocketMode", Options["--fpocketMode"], "All TopN Specify")
 1472 
 1473     FpocketIDsList = []
 1474     if re.match("^(TopN|Specify)$", Options["--fpocketMode"], re.I):
 1475         if Options["--fpocketIDs"] is None:
 1476             MiscUtil.PrintError(
 1477                 'No value specified for "--fpocketIDs" during "%s" of "-f, --fpocketMode" option.'
 1478                 % (Options["--fpocketMode"])
 1479             )
 1480 
 1481         FpocketIDs = re.sub(" ", "", Options["--fpocketIDs"])
 1482         if not FpocketIDs:
 1483             MiscUtil.PrintError(
 1484                 'No valid value specified for "--fpocketIDs" during "%s" of "-f, --fpocketMode" option.'
 1485                 % (Options["--fpocketMode"])
 1486             )
 1487         FpocketIDsWords = FpocketIDs.split(",")
 1488         if len(FpocketIDsWords) == 0:
 1489             MiscUtil.PrintError(
 1490                 'No valid value specified for "--fpocketIDs" during "%s" of "-f, --fpocketMode" option.'
 1491                 % (Options["--fpocketMode"])
 1492             )
 1493 
 1494         if re.match("^TopN$", Options["--fpocketMode"], re.I):
 1495             if len(FpocketIDsWords) > 1:
 1496                 MiscUtil.PrintError(
 1497                     'Number of values specified for "--fpocketIDs" must be 1 during "TopN" of  "-f, --fpocketMode" option.'
 1498                 )
 1499 
 1500         for FpocketID in FpocketIDsWords:
 1501             MiscUtil.ValidateOptionIntegerValue("--fpocketIDs", FpocketID, {">": 0})
 1502             FpocketIDsList.append(FpocketID)
 1503 
 1504     Options["--fpocketIDsList"] = FpocketIDsList
 1505 
 1506     MiscUtil.ValidateOptionIntegerValue("--labelFontID", Options["--labelFontID"], {})
 1507 
 1508     MiscUtil.ValidateOptionTextValue("--pocketColorByPocketNum", Options["--pocketColorByPocketNum"], "yes no")
 1509     MiscUtil.ValidateOptionTextValue("--pocketLabel", Options["--pocketLabel"], "yes no")
 1510     MiscUtil.ValidateOptionTextValue("--pocketLabelType", Options["--pocketLabelType"], "OneLetter ThreeLetter")
 1511 
 1512     MiscUtil.ValidateOptionTextValue("--pocketSurface", Options["--pocketSurface"], "yes no")
 1513     MiscUtil.ValidateOptionTextValue("--surfaceChain", Options["--surfaceChain"], "yes no")
 1514 
 1515     MiscUtil.ValidateOptionFloatValue("--sphereScale", Options["--sphereScale"], {">": 0.0})
 1516     MiscUtil.ValidateOptionFloatValue("--sphereTransparency", Options["--sphereTransparency"], {">=": 0.0, "<=": 1.0})
 1517 
 1518     MiscUtil.ValidateOptionTextValue(
 1519         "--surfaceColorPalette", Options["--surfaceColorPalette"], "RedToWhite WhiteToGreen"
 1520     )
 1521     MiscUtil.ValidateOptionFloatValue("--surfaceTransparency", Options["--surfaceTransparency"], {">=": 0.0, "<=": 1.0})
 1522 
 1523 
 1524 # Setup a usage string for docopt...
 1525 _docoptUsage_ = """
 1526 PyMOLVisualizeFpockets.py - Visualize fpockets for macromolecules.
 1527 
 1528 Usage:
 1529     PyMOLVisualizeFpockets.py [--align <yes or no>] [--alignMethod <align, cealign, super>]
 1530                               [--alignMode <FirstChain or Complex>] [--alignRefFile <filename>]
 1531                               [--chainIDs <First, All or ID1,ID2...>] [--fpocketMode <All, TopN, or Specify>]
 1532                               [--fpocketIDs <Value or Value1,Value2...>] [--fpocketPropertiesAppend <yes or no>]
 1533                               [--labelFontID <number>] [--outfilesDir <outfilesDir>] [--pocketColorByPocketNum <yes or no>]
 1534                               [--pocketLabel <yes or no>] [--pocketLabelType <OneLetter or ThreeLetter>]
 1535                               [--pocketSurface <yes or no>] [--sphereScale <number>] [--sphereTransparency <number>]
 1536                               [--surfaceChain <yes or no>] [--surfaceAtomTypesColors <ColorType,ColorSpec,...>]
 1537                               [--surfaceColor <ColorName>] [--surfaceColorPalette <RedToWhite or WhiteToGreen>]
 1538                               [--surfaceTransparency <number>] [--overwrite] [-w <dir>] -i <infile1,infile2,infile3...> -o <outfile>
 1539     PyMOLVisualizeFpockets.py -h | --help | -e | --examples
 1540 
 1541 Description:
 1542     Generate a PyMOL visualization file for visualizing pockets in macromolecules
 1543     detected by an open source package named Fpocket [ Ref 166 ].
 1544 
 1545     The results of Fpocket calculations must be available in the current directory
 1546     for all input files. A complete set of expected results is shown below:
 1547     
 1548         Dir: <PDBFileRoot>_out
 1549             <PDBFileRoot>_out.pdb
 1550                 ... .. ...
 1551             Dir: pockets
 1552                 <PDBFileRoot><PocketID>_atm.pdb
 1553                 ... .. ...
 1554     
 1555     The supported input file format is: PDB (.pdb)
 1556 
 1557     The supported output file formats is: PyMOL script file (.pml)
 1558 
 1559     The following directory and files are created for the visualization of pockets
 1560     detected by Fpocket:
 1561     
 1562         Dir: <OutFileRoot>_out_PyMOL or <OutfilesDir>
 1563             <OutfileRoot>.pml 
 1564             <PDBFileRoot>.pdb
 1565             <PDBFileRoot>_out.pdb
 1566     
 1567     You may visualize pockets in PyMOL by loading <OutfileRoot>.pml from
 1568     <OutfileRoot>_out_PyMOL or <OutfilesDir> directory.
 1569 
 1570     A variety of PyMOL groups and objects may be  created for visualization of
 1571     fpockets in macromolecules. These groups and objects correspond to complexes,
 1572     chains, fpockets, and surfaces. A complete hierarchy of all possible PyMOL
 1573     groups and objects is shown below:
 1574     
 1575         <PDBFileRoot>
 1576             .Complex
 1577                 .Initial_PDB
 1578                 .Fpocket_PDB
 1579             .Chain<ID>
 1580                 .Complex
 1581                     .Complex
 1582                 .Chain
 1583                     .Chain
 1584                     .Surface
 1585                         .Surface
 1586                         .Hydrophobicity
 1587                         .Hydrophobicity_Charge
 1588                 .FPocket<ID>
 1589                     .FPocket
 1590                     .Residues
 1591                     .Surface
 1592                         .Surface
 1593                         .Hydrophobicity
 1594                         .Hydrophobicity_Charge
 1595             .Chain<ID>
 1596                     ... ... ...
 1597                 .FPocket<ID>
 1598                     ... ... ...
 1599                 .FPocket<ID>
 1600                     ... ... ...
 1601             .Chain<ID>
 1602                 ... ... ...
 1603         <PDBFileRoot>
 1604             .Complex
 1605                 ... ... ...
 1606             .Chain<ID>
 1607                 ... ... ...
 1608                 .FPocket<ID>
 1609                     ... ... ...
 1610                 .FPocket<ID>
 1611                     ... ... ...
 1612             .Chain<ID>
 1613                 ... ... ...
 1614 
 1615 Options:
 1616     -a, --align <yes or no>  [default: no]
 1617         Align input files to a reference file before visualization.
 1618     --alignMethod <align, cealign, super>  [default: super]
 1619         Alignment methodology to use for aligning input files to a
 1620         reference file.
 1621     --alignMode <FirstChain or Complex>  [default: FirstChain]
 1622         Portion of input and reference files to use for spatial alignment of
 1623         input files against reference file.  Possible values: FirstChain or
 1624         Complex.
 1625         
 1626         The FirstChain mode allows alignment of the first chain in each input
 1627         file to the first chain in the reference file along with moving the rest
 1628         of the complex to coordinate space of the reference file. The complete
 1629         complex in each input file is aligned to the complete complex in reference
 1630         file for the Complex mode.
 1631     --alignRefFile <filename>  [default: FirstInputFile]
 1632         Reference input file name. The default is to use the first input file
 1633         name specified using '-i, --infiles' option.
 1634     -c, --chainIDs <First, All or ID1,ID2...>  [default: First]
 1635         List of chain IDs to use for visualizing fpockets in macromolecules. Possible
 1636         values: First, All, or a comma delimited list of chain IDs. The default is to
 1637         use the chain ID for the first chain in each input file.
 1638     -e, --examples
 1639         Print examples.
 1640     -f, --fpocketMode <All, TopN, or Specify>  [default: All]
 1641         Fpockets specification mode for visualizing fpockets across chains in
 1642         macromolecules. Possible values: All, TopN, or specify. By default, all
 1643         available fpockets are visualized across specified chains.
 1644         
 1645         The fpocket IDs must be specified using '--fpocketIDs' option for 'TopN'
 1646         and 'Specifiy' value of '--fpocketMode '.
 1647     --fpocketIDs <Value or Value1,Value2...>
 1648         List of Fpocket IDs for visualizing fpockets across chains. This value is
 1649         dependent on the value of '--fpocketMode'. The possible values are
 1650         either a number or a comma delimited list of number for 'TopN' and 
 1651         'Specify' value '--fpocketMode' option. For example:
 1652         
 1653         This option is ignored during 'All' value of '--fpocketMode' option.
 1654     --fpocketPropertiesAppend <yes or no>  [default: yes]
 1655         Append fpocket properties to names of PyMOL fpocket groups and
 1656         objects. The following properties are appended to the names of PyMOL
 1657          groups using their abbreviations and values:  PocketScore - S; DrugScore - D;
 1658          PocketVolume - V; HydrophobicityScore - H;  PolarityScore - P.
 1659          For example:
 1660          
 1661          Fpocket1_S0p50_D0p00_V638p81_Hneg6p67_P11p00.Fpocket
 1662     -h, --help
 1663         Print this help message.
 1664     -i, --infiles <infile1,infile2,infile3...>
 1665         Input PDB file names. The current directory must contain the results from
 1666         Fpocket calculations for all the input PDB files.
 1667     --labelFontID <number>  [default: 7]
 1668         Font ID for drawing labels. Default: 7 (Sans Bold). Valid values: 5 to 16.
 1669         The specified value must be a valid PyMOL font ID. No validation is
 1670         performed. The complete lists of valid font IDs is available at:
 1671         pymolwiki.org/index.php/Label_font_id. Examples: 5 - Sans;
 1672         7 - Sans Bold; 9 - Serif; 10 - Serif Bold.
 1673     -o, --outfile <outfile>
 1674         PML output file name for visualizing fpockets. The PML outfile is created
 1675         in a new output directory named <OutfileRoot>_out_PyMOL. In addition, the
 1676         output directory contains all appropriate PDB files generated by Fpocket for
 1677         visualization of fpockets in PyMOL.
 1678     --outfilesDir <outfilesDir>  [default: auto]
 1679         Output files directory name. Default: <OutfileRoot>_out_PyMOL.
 1680     --pocketColorByPocketNum <yes or no>  [default: yes]
 1681         Color fpocket residues and residue labels by pocket number. Otherwise,
 1682         the pocket residues are colored by element names using default PyMOL
 1683         color scheme. No color is set for residue labels.
 1684     --pocketLabel <yes or no>  [default: yes]
 1685         Display residue labels on fpocket residues. The residue number is always
 1686         appended to residue label. You may specify a one or thee letter residue
 1687         labels using '--pocketLabelType' option.
 1688     --pocketLabelType <OneLetter or ThreeLetter>  [default: OneLetter]
 1689         Display one or three letter residue labels on fpocket residues
 1690     --pocketSurface <yes or no>  [default: yes]
 1691         Surfaces around fpocket residues colored by hydrophobicity alone and
 1692         both hydrophobicity and charge. The hydrophobicity surface is colored
 1693         at residue level using Eisenberg hydrophobicity scale for residues and color
 1694         gradient specified by '--surfaceColorPalette' option. The  hydrophobicity and
 1695         charge surface is colored at atom level using colors specified for
 1696         groups of atoms by '--surfaceAtomTypesColors' option. This scheme allows
 1697         simultaneous mapping of hyrophobicity and charge values on the surfaces.
 1698         
 1699         In addition, generic surfaces colored by '--surfaceColor' are always created
 1700         for pockets.
 1701     --sphereScale <number>  [default: 0.4]
 1702         Scaling factor for spheres used to display fpocket alpha spheres.
 1703     --sphereTransparency <number>  [default: 0.25]
 1704         Transparency for spheres used to display fpocket alpha spheres.
 1705     --surfaceChain <yes or no>  [default: yes]
 1706         Surfaces around individual chain colored by hydrophobicity alone and
 1707         both hydrophobicity and charge. The hydrophobicity surface is colored
 1708         at residue level using Eisenberg hydrophobicity scale for residues and color
 1709         gradient specified by '--surfaceColorPalette' option. The  hydrophobicity and
 1710         charge surface is colored at atom level using colors specified for
 1711         groups of atoms by '--surfaceAtomTypesColors' option. This scheme allows
 1712         simultaneous mapping of hyrophobicity and charge values on the surfaces.
 1713         
 1714         In addition, generic surfaces colored by '--surfaceColor' are always created
 1715         for chains.
 1716     --surfaceAtomTypesColors <ColorType,ColorSpec,...>  [default: auto]
 1717         Atom colors for generating surfaces colored by hyrophobicity and charge
 1718         around chains and pockets in proteins. It's a pairwise comma delimited list
 1719         of atom color type and color specification for groups of atoms.
 1720         
 1721         The default values for color types  along wth color specifications
 1722         are shown below: 
 1723             
 1724             HydrophobicAtomsColor, yellow,
 1725             NegativelyChargedAtomsColor, red,
 1726             PositivelyChargedAtomsColor, blue,
 1727             OtherAtomsColor, gray90
 1728             
 1729         The color names must be valid PyMOL names.
 1730         
 1731         The color values may also be specified as space delimited RGB triplets:
 1732              
 1733             HydrophobicAtomsColor, 0.95 0.78 0.0,
 1734             NegativelyChargedAtomsColor, 1.0 0.4 0.4,
 1735             PositivelyChargedAtomsColor, 0.2 0.5 0.8,
 1736             OtherAtomsColor, 0.95 0.95 0.95
 1737             
 1738     --surfaceColor <ColorName>  [default: lightblue]
 1739         Color name for surfaces around chains and pockets. This color is not used
 1740         for surfaces colored by hydrophobicity and charge. The color name must be
 1741         a valid PyMOL name.
 1742     --surfaceColorPalette <RedToWhite or WhiteToGreen>  [default: RedToWhite]
 1743         Color palette for hydrophobic surfaces around chains and pockets in proteins.
 1744         Possible values: RedToWhite or WhiteToGreen from most hydrophobic amino
 1745         acid to least hydrophobic. The colors values for amino acids are taken from
 1746         color_h script available as part of the Script Library at PyMOL Wiki.
 1747     --surfaceTransparency <number>  [default: 0.25]
 1748         Surface transparency for molecular surfaces.
 1749     --overwrite
 1750         Overwrite existing files.
 1751     -w, --workingdir <dir>
 1752         Location of working directory which defaults to the current directory.
 1753 
 1754 Examples:
 1755     To visualize all fpockets available in a directory <PDBRoot>_out for the first
 1756     chain, along pocket residues and surfaces, in a PDB file, and generate a PML
 1757     file in a new directory <PDBRoot>_out_pymol, type:
 1758 
 1759         % PyMOLVisualizeFpockets.py -i Sample5.pdb -o Sample5.pml
 1760 
 1761     To rerun the first example without displaying pocket residue labels,
 1762     coloring pockets by element type, and write out a PML file, type:
 1763 
 1764         % PyMOLVisualizeFpockets.py --pocketColorByPocketNum no
 1765           --pocketLabel no -i Sample5.pdb -o Sample5.pml
 1766 
 1767     To rerun the first example to visualize only top 5 fpockets and write out a
 1768     PML file, type:
 1769 
 1770         % PyMOLVisualizeFpockets.py -f TopN --fpocketIDs 5 -i Sample5.pdb
 1771           -o Sample5.pml
 1772 
 1773     To rerun the first example to visualize a specific set of fpockets and write
 1774     out a PML file, type:
 1775 
 1776         % PyMOLVisualizeFpockets.py -f Specify --fpocketIDs "1,2,3" -i Sample5.pdb
 1777           -o Sample5.pml
 1778 
 1779    To rerun the first example to visualize all fpockets across all chains and write
 1780    out a PML file, type:
 1781 
 1782         % PyMOLVisualizeFpockets.py -c All -f All -i Sample5.pdb -o Sample5.pml
 1783 
 1784     To rerun the first example without displaying hydrophobic and charge surfaces
 1785     around chain and pockets and and write out a PML file, type:
 1786 
 1787         % PyMOLVisualizeFpockets.py --surfaceChain no  --pocketSurface no
 1788           -i Sample5.pdb -o Sample5.pml
 1789 
 1790     To visualize top 5 fpockets available in a directories <PDBRoot>_out for the
 1791     first chain, along pocket residues and surfaces, in PDB files, aligning first
 1792     fchain in each input file to the first chain in first input file, and generate a
 1793     PML file in a new directory <PDBRoot>_out_pymol, type:
 1794 
 1795         % PyMOLVisualizeFpockets.py -f TopN --fpocketIDs 5 --align yes
 1796           -i "Sample5.pdb,Sample6.pdb" -o Sample5Aligned.pml
 1797 
 1798     To visualize top 5 fpockets available in a directories <PDBRoot>_out for the
 1799     first chain, along pocket residues and surfaces, in PDB files, aligning first
 1800     chain in each input file to the first chain in first chain in a specified PDB
 1801     file using a specified alignment method,, and generate a PML file in a new
 1802     directory <PDBRoot>_out_pymol, type:
 1803 
 1804         % PyMOLVisualizeFpockets.py -f TopN --fpocketIDs 5 --align yes
 1805           --alignMode FirstChain --alignRefFile Sample6.pdb --alignMethod super 
 1806           -i "Sample5.pdb,Sample6.pdb" -o Sample5Aligned.pml
 1807 
 1808 Author:
 1809     Manish Sud
 1810 
 1811 Collaborators:
 1812     Joann Prescott-Roy and Pat Walters
 1813 
 1814 See also:
 1815     DownloadPDBFiles.pl, PyMOLVisualizeCavities.py,
 1816     PyMOLVisualizeCryoEMDensity.py, PyMOLVisualizeElectronDensity.py,
 1817     PyMOLVisualizeInterfaces.py, PyMOLVisualizeMacromolecules.py,
 1818     PyMOLVisualizeSurfaceAndBuriedResidues.py
 1819 
 1820 Copyright:
 1821     Copyright (C) 2026 Manish Sud. All rights reserved.
 1822 
 1823     The functionality available in this script is implemented using PyMOL, a
 1824     molecular visualization system on an open source foundation originally
 1825     developed by Warren DeLano.
 1826 
 1827     This file is part of MayaChemTools.
 1828 
 1829     MayaChemTools is free software; you can redistribute it and/or modify it under
 1830     the terms of the GNU Lesser General Public License as published by the Free
 1831     Software Foundation; either version 3 of the License, or (at your option) any
 1832     later version.
 1833 
 1834 """
 1835 
 1836 if __name__ == "__main__":
 1837     main()