MayaChemTools

    1 #!/bin/env python
    2 #
    3 # File: PyMOLVisualizeCryoEMDensity.py
    4 # Author: Manish Sud <msud@san.rr.com>
    5 #
    6 # Copyright (C) 2025 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 import xml.etree.ElementTree as ElementTree
   37 
   38 # PyMOL imports...
   39 try:
   40     import pymol
   41 
   42     # Finish launching PyMOL in  a command line mode for batch processing (-c)
   43     # along with the following options:  disable loading of pymolrc and plugins (-k);
   44     # suppress start up messages (-q)
   45     pymol.finish_launching(["pymol", "-ckq"])
   46 except ImportError as ErrMsg:
   47     sys.stderr.write("\nFailed to import PyMOL module/package: %s\n" % ErrMsg)
   48     sys.stderr.write("Check/update your PyMOL environment and try again.\n\n")
   49     sys.exit(1)
   50 
   51 # MayaChemTools imports...
   52 sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), "..", "lib", "Python"))
   53 try:
   54     from docopt import docopt
   55     import MiscUtil
   56     import PyMOLUtil
   57 except ImportError as ErrMsg:
   58     sys.stderr.write("\nFailed to import MayaChemTools module/package: %s\n" % ErrMsg)
   59     sys.stderr.write("Check/update your MayaChemTools environment and try again.\n\n")
   60     sys.exit(1)
   61 
   62 ScriptName = os.path.basename(sys.argv[0])
   63 Options = {}
   64 OptionsInfo = {}
   65 
   66 
   67 def main():
   68     """Start execution of the script."""
   69 
   70     MiscUtil.PrintInfo(
   71         "\n%s (PyMOL v%s; MayaChemTools v%s; %s): Starting...\n"
   72         % (ScriptName, pymol.cmd.get_version()[0], MiscUtil.GetMayaChemToolsVersion(), time.asctime())
   73     )
   74 
   75     (WallClockTime, ProcessorTime) = MiscUtil.GetWallClockAndProcessorTime()
   76 
   77     # Retrieve command line arguments and options...
   78     RetrieveOptions()
   79 
   80     # Process and validate command line arguments and options...
   81     ProcessOptions()
   82 
   83     # Perform actions required by the script...
   84     GenerateCryoEMDensityVisualization()
   85 
   86     MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName)
   87     MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime))
   88 
   89 
   90 def GenerateCryoEMDensityVisualization():
   91     """Generate cryo-EM density visualization."""
   92 
   93     Outfile = OptionsInfo["PMLOutfile"]
   94     OutFH = open(Outfile, "w")
   95     if OutFH is None:
   96         MiscUtil.PrintError("Failed to open output fie %s " % Outfile)
   97 
   98     MiscUtil.PrintInfo("\nGenerating file %s..." % Outfile)
   99 
  100     # Setup header...
  101     WritePMLHeader(OutFH, ScriptName)
  102     WritePyMOLParameters(OutFH)
  103 
  104     # Load reffile for alignment..
  105     if OptionsInfo["Align"]:
  106         WriteAlignReference(OutFH)
  107 
  108     # Setup view for each input file...
  109     FirstComplex = True
  110     FirstComplexFirstChainName = None
  111     for FileIndex in range(0, len(OptionsInfo["InfilesInfo"]["InfilesNames"])):
  112         # Setup PyMOL object names...
  113         PyMOLObjectNames = SetupPyMOLObjectNames(FileIndex)
  114 
  115         # Setup complex view...
  116         WriteComplexView(OutFH, FileIndex, PyMOLObjectNames, FirstComplex)
  117 
  118         SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
  119         FirstChain = True
  120         for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
  121             if FirstComplex and FirstChain:
  122                 FirstComplexFirstChainName = PyMOLObjectNames["Chains"][ChainID]["ChainAlone"]
  123 
  124             WriteChainView(OutFH, FileIndex, PyMOLObjectNames, ChainID)
  125 
  126             # Setup ligand views...
  127             FirstLigand = True
  128             for LigandID in SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID]:
  129                 WriteChainLigandView(OutFH, FileIndex, PyMOLObjectNames, ChainID, LigandID)
  130 
  131                 # Set up ligand level group...
  132                 Enable, Action = [False, "close"]
  133                 if FirstLigand:
  134                     FirstLigand = False
  135                     Enable, Action = [True, "open"]
  136                 GenerateAndWritePMLForGroup(
  137                     OutFH,
  138                     PyMOLObjectNames["Ligands"][ChainID][LigandID]["ChainLigandGroup"],
  139                     PyMOLObjectNames["Ligands"][ChainID][LigandID]["ChainLigandGroupMembers"],
  140                     Enable,
  141                     Action,
  142                 )
  143 
  144             # Setup Chain level group...
  145             Enable, Action = [False, "close"]
  146             if FirstChain:
  147                 FirstChain = False
  148                 Enable, Action = [True, "open"]
  149             GenerateAndWritePMLForGroup(
  150                 OutFH,
  151                 PyMOLObjectNames["Chains"][ChainID]["ChainGroup"],
  152                 PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"],
  153                 Enable,
  154                 Action,
  155             )
  156 
  157         # Set up complex level group...
  158         Enable, Action = [False, "close"]
  159         if FirstComplex:
  160             FirstComplex = False
  161             Enable, Action = [True, "open"]
  162         GenerateAndWritePMLForGroup(
  163             OutFH, PyMOLObjectNames["PDBGroup"], PyMOLObjectNames["PDBGroupMembers"], Enable, Action
  164         )
  165 
  166         # Delete empty PyMOL objects...
  167         DeleteEmptyPyMOLObjects(OutFH, FileIndex, PyMOLObjectNames)
  168 
  169     if OptionsInfo["Align"]:
  170         DeleteAlignReference(OutFH)
  171 
  172     if FirstComplexFirstChainName is not None:
  173         OutFH.write("""\ncmd.orient("%s", animate = -1)\n""" % FirstComplexFirstChainName)
  174     else:
  175         OutFH.write("""\ncmd.orient("visible", animate = -1)\n""")
  176 
  177     OutFH.close()
  178 
  179     # Generate PSE file as needed...
  180     if OptionsInfo["PSEOut"]:
  181         GeneratePyMOLSessionFile()
  182 
  183 
  184 def WritePMLHeader(OutFH, ScriptName):
  185     """Write out PML setting up complex view."""
  186 
  187     HeaderInfo = PyMOLUtil.SetupPMLHeaderInfo(ScriptName)
  188     OutFH.write("%s\n" % HeaderInfo)
  189 
  190 
  191 def WritePyMOLParameters(OutFH):
  192     """Write out PyMOL global parameters."""
  193 
  194     PMLCmds = []
  195     PMLCmds.append("""cmd.set("mesh_width", %.2f)""" % (OptionsInfo["MeshWidth"]))
  196     PMLCmds.append("""cmd.set("transparency", %.2f, "", 0)""" % (OptionsInfo["SurfaceTransparency"]))
  197     PMLCmds.append("""cmd.set("label_font_id", %s)""" % (OptionsInfo["LabelFontID"]))
  198 
  199     if OptionsInfo["VolumeColorRampCreate"]:
  200         ColorRampName = OptionsInfo["VolumeColorRampName"]
  201         ContourLevel = OptionsInfo["VolumeColorRampContourLevel"]
  202         LowerContourLevel = ContourLevel - 0.3
  203         UpperContourLevel = ContourLevel + 0.3
  204         PMLCmds.append(
  205             """cmd.volume_ramp_new("%s", "%.2f blue 0.00 %.2f cyan 0.20 %.2f blue 0.00")"""
  206             % (ColorRampName, LowerContourLevel, ContourLevel, UpperContourLevel)
  207         )
  208 
  209     PML = "\n".join(PMLCmds)
  210 
  211     OutFH.write("""\n""\n"Setting up PyMOL gobal parameters..."\n""\n""")
  212     OutFH.write("%s\n" % PML)
  213 
  214 
  215 def WriteAlignReference(OutFH):
  216     """Setup object for alignment reference."""
  217 
  218     RefFileInfo = OptionsInfo["RefFileInfo"]
  219     RefFile = RefFileInfo["RefFileName"]
  220     RefName = RefFileInfo["PyMOLObjectName"]
  221 
  222     PMLCmds = []
  223     PMLCmds.append("""cmd.load("%s", "%s")""" % (RefFile, RefName))
  224     PMLCmds.append("""cmd.hide("everything", "%s")""" % (RefName))
  225     PMLCmds.append("""cmd.disable("%s")""" % (RefName))
  226     PML = "\n".join(PMLCmds)
  227 
  228     OutFH.write("""\n""\n"Loading %s and setting up view for align reference..."\n""\n""" % RefFile)
  229     OutFH.write("%s\n" % PML)
  230 
  231 
  232 def WriteAlignComplex(OutFH, FileIndex, PyMOLObjectNames):
  233     """Setup alignment of complex to reference."""
  234 
  235     RefFileInfo = OptionsInfo["RefFileInfo"]
  236     RefName = RefFileInfo["PyMOLObjectName"]
  237 
  238     ComplexName = PyMOLObjectNames["Complex"]
  239 
  240     if re.match("^FirstChain$", OptionsInfo["AlignMode"], re.I):
  241         RefFirstChainID = RefFileInfo["ChainsAndLigandsInfo"]["ChainIDs"][0]
  242         RefAlignSelection = "%s and chain %s" % (RefName, RefFirstChainID)
  243 
  244         ComplexFirstChainID = RetrieveFirstChainID(FileIndex)
  245         ComplexAlignSelection = "%s and chain %s" % (ComplexName, ComplexFirstChainID)
  246     else:
  247         RefAlignSelection = RefName
  248         ComplexAlignSelection = ComplexName
  249 
  250     PML = PyMOLUtil.SetupPMLForAlignment(OptionsInfo["AlignMethod"], RefAlignSelection, ComplexAlignSelection)
  251     OutFH.write("""\n""\n"Aligning %s against reference %s ..."\n""\n""" % (ComplexAlignSelection, RefAlignSelection))
  252     OutFH.write("%s\n" % PML)
  253 
  254 
  255 def DeleteAlignReference(OutFH):
  256     """Delete alignment reference object."""
  257 
  258     RefName = OptionsInfo["RefFileInfo"]["PyMOLObjectName"]
  259     OutFH.write("""\n""\n"Deleting alignment reference object %s..."\n""\n""" % RefName)
  260     OutFH.write("""cmd.delete("%s")\n""" % RefName)
  261 
  262 
  263 def WriteComplexView(OutFH, FileIndex, PyMOLObjectNames, FirstComplex):
  264     """Write out PML for viewing polymer complex along with cryo-EM density."""
  265 
  266     # Setup complex...
  267     Infile = OptionsInfo["InfilesInfo"]["InfilesNames"][FileIndex]
  268     PML = PyMOLUtil.SetupPMLForPolymerComplexView(PyMOLObjectNames["Complex"], Infile, True)
  269     OutFH.write("""\n""\n"Loading %s and setting up view for complex..."\n""\n""" % Infile)
  270     OutFH.write("%s\n" % PML)
  271 
  272     if OptionsInfo["Align"]:
  273         # No need to align complex on to itself...
  274         if not (re.match("^FirstInputFile$", OptionsInfo["AlignRefFile"], re.I) and FirstComplex):
  275             WriteAlignComplex(OutFH, FileIndex, PyMOLObjectNames)
  276 
  277     # Setup cryo-EM density maps and meshes...
  278     DensityMapFile = OptionsInfo["DensityMapFilesNames"][FileIndex]
  279     ContourLevel = OptionsInfo["MeshLevels"][FileIndex]
  280     WriteComplexCryoEMDensityMapView(OutFH, PyMOLObjectNames, DensityMapFile, ContourLevel)
  281 
  282     # Setup complex group...
  283     GenerateAndWritePMLForGroup(
  284         OutFH, PyMOLObjectNames["ComplexGroup"], PyMOLObjectNames["ComplexGroupMembers"], False, "close"
  285     )
  286 
  287 
  288 def WriteComplexCryoEMDensityMapView(OutFH, PyMOLObjectNames, MapFileName, ContourLevel):
  289     """Write out PML for viewing cryoEM density map."""
  290 
  291     # Load cryo-EM density map and setup mesh views...
  292     Info = (
  293         """\
  294 ""
  295 "Loading cryo-EM density map %s and setting up mesh view for complex..."
  296 "" """
  297         % MapFileName
  298     )
  299     OutFH.write("\n%s\n" % Info)
  300 
  301     MapName = PyMOLObjectNames["ComplexCryoEMMap"]
  302     ComplexName = PyMOLObjectNames["Complex"]
  303 
  304     Color = OptionsInfo["MeshColor"]
  305     VolumeColorRamp = OptionsInfo["VolumeColorRampName"]
  306 
  307     VolumeName = PyMOLObjectNames["ComplexCryoEMVolume"]
  308     MeshName = PyMOLObjectNames["ComplexCryoEMMesh"]
  309     SurfaceName = PyMOLObjectNames["ComplexCryoEMSurface"]
  310 
  311     AlignMapToObjectName = ComplexName if OptionsInfo["Align"] else None
  312     EnableMap = True
  313     PML = SetupPMLForCryoEMDensityMap(MapFileName, MapName, AlignMapToObjectName, EnableMap)
  314     OutFH.write("%s\n" % PML)
  315 
  316     EnableMesh = OptionsInfo["MeshComplex"]
  317 
  318     EnableVolume = OptionsInfo["VolumeComplex"]
  319     if EnableVolume and EnableMesh:
  320         EnableVolume = False
  321 
  322     EnableSurface = OptionsInfo["SurfaceComplex"]
  323     if EnableSurface and (EnableVolume or EnableMesh):
  324         EnableSurface = False
  325 
  326     if OptionsInfo["VolumeComplex"]:
  327         PML = SetupPMLForCryoEMDensityVolume(
  328             MapName, VolumeName, VolumeColorRamp, Enable=EnableVolume, Selection=ComplexName
  329         )
  330         OutFH.write("\n%s\n" % PML)
  331 
  332     if OptionsInfo["MeshComplex"]:
  333         PML = SetupPMLForCryoEMDensityMesh(
  334             MapName, MeshName, ContourLevel, Color, Enable=EnableMesh, Selection=ComplexName
  335         )
  336         OutFH.write("\n%s\n" % PML)
  337 
  338     if OptionsInfo["SurfaceComplex"]:
  339         PML = SetupPMLForCryoEMDensitySurface(
  340             MapName, SurfaceName, ContourLevel, Color, Enable=EnableSurface, Selection=ComplexName
  341         )
  342         OutFH.write("\n%s\n" % PML)
  343 
  344     GenerateAndWritePMLForGroup(
  345         OutFH, PyMOLObjectNames["ComplexCryoEMGroup"], PyMOLObjectNames["ComplexCryoEMGroupMembers"], True, "close"
  346     )
  347 
  348 
  349 def WriteChainView(OutFH, FileIndex, PyMOLObjectNames, ChainID):
  350     """Write out PML for viewing chain."""
  351 
  352     OutFH.write("""\n""\n"Setting up views for chain %s..."\n""\n""" % ChainID)
  353 
  354     ChainComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
  355 
  356     # Setup chain complex group view...
  357     WriteChainComplexAndMeshViews(OutFH, FileIndex, PyMOLObjectNames, ChainID)
  358 
  359     # Setup chain view...
  360     WriteChainAloneViews(OutFH, FileIndex, PyMOLObjectNames, ChainID)
  361 
  362     # Setup chain solvent view...
  363     PML = PyMOLUtil.SetupPMLForSolventView(PyMOLObjectNames["Chains"][ChainID]["Solvent"], ChainComplexName, False)
  364     OutFH.write("\n%s\n" % PML)
  365 
  366     # Setup chain inorganic view...
  367     PML = PyMOLUtil.SetupPMLForInorganicView(PyMOLObjectNames["Chains"][ChainID]["Inorganic"], ChainComplexName, False)
  368     OutFH.write("\n%s\n" % PML)
  369 
  370 
  371 def WriteChainComplexAndMeshViews(OutFH, FileIndex, PyMOLObjectNames, ChainID):
  372     """Write chain complex and mesh views."""
  373 
  374     # Setup chain complex...
  375     ChainComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
  376     PML = PyMOLUtil.SetupPMLForPolymerChainComplexView(ChainComplexName, PyMOLObjectNames["Complex"], ChainID, True)
  377     OutFH.write("%s\n" % PML)
  378 
  379     SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
  380 
  381     MeshChainComplex = SpecifiedChainsAndLigandsInfo["MeshChainComplex"][ChainID]
  382     VolumeChainComplex = SpecifiedChainsAndLigandsInfo["VolumeChainComplex"][ChainID]
  383     SurfaceChainComplex = SpecifiedChainsAndLigandsInfo["SurfaceChainComplex"][ChainID]
  384 
  385     EnableVolumeChainComplex = SpecifiedChainsAndLigandsInfo["EnableVolumeChainComplex"][ChainID]
  386     EnableMeshChainComplex = SpecifiedChainsAndLigandsInfo["EnableMeshChainComplex"][ChainID]
  387     EnableSurfaceChainComplex = SpecifiedChainsAndLigandsInfo["EnableSurfaceChainComplex"][ChainID]
  388 
  389     if MeshChainComplex or VolumeChainComplex or SurfaceChainComplex:
  390         # Set up cryoEM mesh and group...
  391         MapName = PyMOLObjectNames["ComplexCryoEMMap"]
  392         ContourLevel = OptionsInfo["MeshLevels"][FileIndex]
  393         Color = OptionsInfo["MeshColor"]
  394 
  395         VolumeColorRamp = OptionsInfo["VolumeColorRampName"]
  396 
  397         VolumeName = PyMOLObjectNames["Chains"][ChainID]["ChainComplexCryoEMVolume"]
  398         MeshName = PyMOLObjectNames["Chains"][ChainID]["ChainComplexCryoEMMesh"]
  399         SurfaceName = PyMOLObjectNames["Chains"][ChainID]["ChainComplexCryoEMSurface"]
  400 
  401         if VolumeChainComplex:
  402             PML = SetupPMLForCryoEMDensityVolume(
  403                 MapName, VolumeName, VolumeColorRamp, Enable=EnableVolumeChainComplex, Selection=ChainComplexName
  404             )
  405             OutFH.write("\n%s\n" % PML)
  406 
  407         if MeshChainComplex:
  408             PML = SetupPMLForCryoEMDensityMesh(
  409                 MapName, MeshName, ContourLevel, Color, Enable=EnableMeshChainComplex, Selection=ChainComplexName
  410             )
  411             OutFH.write("\n%s\n" % PML)
  412 
  413         if SurfaceChainComplex:
  414             PML = SetupPMLForCryoEMDensitySurface(
  415                 MapName, SurfaceName, ContourLevel, Color, Enable=EnableSurfaceChainComplex, Selection=ChainComplexName
  416             )
  417             OutFH.write("\n%s\n" % PML)
  418 
  419         GenerateAndWritePMLForGroup(
  420             OutFH,
  421             PyMOLObjectNames["Chains"][ChainID]["ChainComplexCryoEMGroup"],
  422             PyMOLObjectNames["Chains"][ChainID]["ChainComplexCryoEMGroupMembers"],
  423             True,
  424             "close",
  425         )
  426 
  427     # Setup chain complex group...
  428     EnableChainComplexGroup = SpecifiedChainsAndLigandsInfo["EnableChainComplexGroup"][ChainID]
  429     GenerateAndWritePMLForGroup(
  430         OutFH,
  431         PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroup"],
  432         PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"],
  433         EnableChainComplexGroup,
  434         "close",
  435     )
  436 
  437 
  438 def WriteChainAloneViews(OutFH, FileIndex, PyMOLObjectNames, ChainID):
  439     """Write individual chain views."""
  440 
  441     ChainComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
  442 
  443     # Setup chain view...
  444     ChainName = PyMOLObjectNames["Chains"][ChainID]["ChainAlone"]
  445     PML = PyMOLUtil.SetupPMLForPolymerChainView(ChainName, ChainComplexName, Enable=True)
  446     OutFH.write("\n%s\n" % PML)
  447 
  448     # Setup chain putty by B-factor view...
  449     if OptionsInfo["BFactorChainCartoonPutty"]:
  450         BFactorPuttyName = PyMOLObjectNames["Chains"][ChainID]["ChainAloneBFactorPutty"]
  451         PML = PyMOLUtil.SetupPMLForBFactorPuttyView(
  452             BFactorPuttyName, ChainName, ColorPalette=OptionsInfo["BFactorColorPalette"], Enable=False
  453         )
  454         OutFH.write("\n%s\n" % PML)
  455 
  456     # Setup chain selections view...
  457     SetupChainSelectionsView(OutFH, FileIndex, PyMOLObjectNames, ChainID)
  458 
  459     # Setup chain group...
  460     SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
  461     EnableChainAloneGroup = SpecifiedChainsAndLigandsInfo["EnableChainAloneGroup"][ChainID]
  462     GenerateAndWritePMLForGroup(
  463         OutFH,
  464         PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroup"],
  465         PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"],
  466         EnableChainAloneGroup,
  467         "close",
  468     )
  469 
  470 
  471 def SetupChainSelectionsView(OutFH, FileIndex, PyMOLObjectNames, ChainID):
  472     """Setup chain selectons view."""
  473 
  474     if not OptionsInfo["ChainSelections"]:
  475         return
  476 
  477     ChainName = PyMOLObjectNames["Chains"][ChainID]["ChainAlone"]
  478     SelectionsGroupIDPrefix = "ChainAloneSelections"
  479 
  480     for Index in range(0, len(OptionsInfo["ChainSelectionsInfo"]["Names"])):
  481         SelectionName = OptionsInfo["ChainSelectionsInfo"]["Names"][Index]
  482         SpecifiedSelection = OptionsInfo["ChainSelectionsInfo"]["Selections"][Index]
  483 
  484         SelectionNameGroupID = SelectionName
  485 
  486         # Setup selection object...
  487         SelectionObjectID = "%s%sSelection" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
  488         SelectionObjectName = PyMOLObjectNames["Chains"][ChainID][SelectionObjectID]
  489         SelectionCmd = "(%s and (%s))" % (ChainName, SpecifiedSelection)
  490         PML = PyMOLUtil.SetupPMLForSelectionDisplayView(
  491             SelectionObjectName, SelectionCmd, OptionsInfo["SelectionsChainStyle"], Enable=True
  492         )
  493         OutFH.write("\n%s\n" % PML)
  494 
  495         # Set up cryo-EM mesh and group...
  496         CryoEMVolumeID = "%s%sCryoEMVolume" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
  497         CryoEMMeshID = "%s%sCryoEMMesh" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
  498         CryoEMSurfaceID = "%s%sCryoEMSurface" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
  499         CryoEMMeshGroupID = "%s%sCryoEMGroup" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
  500         CryoEMMeshGroupMembersID = "%s%sCryoEMGroupMembers" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
  501 
  502         CryoEMVolumeName = PyMOLObjectNames["Chains"][ChainID][CryoEMVolumeID]
  503         CryoEMMeshName = PyMOLObjectNames["Chains"][ChainID][CryoEMMeshID]
  504         CryoEMSurfaceName = PyMOLObjectNames["Chains"][ChainID][CryoEMSurfaceID]
  505         CryoEMMeshGroupName = PyMOLObjectNames["Chains"][ChainID][CryoEMMeshGroupID]
  506         CryoEMMeshGroupMembers = PyMOLObjectNames["Chains"][ChainID][CryoEMMeshGroupMembersID]
  507 
  508         MapName = PyMOLObjectNames["ComplexCryoEMMap"]
  509         ContourLevel = OptionsInfo["MeshLevels"][FileIndex]
  510         Color = OptionsInfo["MeshColor"]
  511 
  512         PML = SetupPMLForCryoEMDensityVolume(
  513             MapName, CryoEMVolumeName, OptionsInfo["VolumeColorRampName"], Enable=False, Selection=SelectionObjectName
  514         )
  515         OutFH.write("\n%s\n" % PML)
  516 
  517         PML = SetupPMLForCryoEMDensityMesh(
  518             MapName, CryoEMMeshName, ContourLevel, Color, Enable=True, Selection=SelectionObjectName
  519         )
  520         OutFH.write("\n%s\n" % PML)
  521 
  522         PML = SetupPMLForCryoEMDensitySurface(
  523             MapName, CryoEMSurfaceName, ContourLevel, Color, Enable=False, Selection=SelectionObjectName
  524         )
  525         OutFH.write("\n%s\n" % PML)
  526 
  527         GenerateAndWritePMLForGroup(OutFH, CryoEMMeshGroupName, CryoEMMeshGroupMembers, True, "close")
  528 
  529         # Setup groups for named selections...
  530         SelectionsNameGroupID = "%s%sGroup" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
  531         SelectionsNameGroupMembersID = "%s%sGroupMembers" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
  532         GenerateAndWritePMLForGroup(
  533             OutFH,
  534             PyMOLObjectNames["Chains"][ChainID][SelectionsNameGroupID],
  535             PyMOLObjectNames["Chains"][ChainID][SelectionsNameGroupMembersID],
  536             True,
  537             "open",
  538         )
  539 
  540     # Setup a group for selections...
  541     SelectionsGroupID = "%sGroup" % (SelectionsGroupIDPrefix)
  542     SelectionsGroupMembersID = "%sGroupMembers" % (SelectionsGroupIDPrefix)
  543     GenerateAndWritePMLForGroup(
  544         OutFH,
  545         PyMOLObjectNames["Chains"][ChainID][SelectionsGroupID],
  546         PyMOLObjectNames["Chains"][ChainID][SelectionsGroupMembersID],
  547         False,
  548         "close",
  549     )
  550 
  551 
  552 def WriteChainLigandView(OutFH, FileIndex, PyMOLObjectNames, ChainID, LigandID):
  553     """Write out PML for viewing ligand in a chain."""
  554 
  555     for GroupID in ["Ligand", "Pocket", "PocketSolvent", "PocketInorganic"]:
  556         ComplexName = PyMOLObjectNames["Chains"][ChainID]["ChainComplex"]
  557         LigandName = PyMOLObjectNames["Ligands"][ChainID][LigandID]["Ligand"]
  558 
  559         # Setup main object...
  560         GroupTypeObjectID = "%s" % (GroupID)
  561         GroupTypeObjectName = PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupTypeObjectID]
  562 
  563         if re.match("^Ligand$", GroupID, re.I):
  564             OutFH.write("""\n""\n"Setting up views for ligand %s in chain %s..."\n""\n""" % (LigandID, ChainID))
  565             PML = PyMOLUtil.SetupPMLForLigandView(
  566                 GroupTypeObjectName, ComplexName, LigandID, Enable=True, IgnoreHydrogens=OptionsInfo["IgnoreHydrogens"]
  567             )
  568             OutFH.write("%s\n" % PML)
  569         elif re.match("^Pocket$", GroupID, re.I):
  570             OutFH.write(
  571                 """\n""\n"Setting up views for pocket around ligand %s in chain %s..."\n""\n""" % (LigandID, ChainID)
  572             )
  573             PML = PyMOLUtil.SetupPMLForLigandPocketView(
  574                 GroupTypeObjectName,
  575                 ComplexName,
  576                 LigandName,
  577                 OptionsInfo["PocketDistanceCutoff"],
  578                 Enable=True,
  579                 IgnoreHydrogens=OptionsInfo["IgnoreHydrogens"],
  580             )
  581             OutFH.write("%s\n" % PML)
  582             OutFH.write(
  583                 """cmd.set("label_color", "%s", "%s")\n""" % (OptionsInfo["PocketLabelColor"], GroupTypeObjectName)
  584             )
  585         elif re.match("^PocketSolvent$", GroupID, re.I):
  586             OutFH.write(
  587                 """\n""\n"Setting up views for solvent in pockect around ligand %s in chain %s..."\n""\n"""
  588                 % (LigandID, ChainID)
  589             )
  590             PML = PyMOLUtil.SetupPMLForLigandPocketSolventView(
  591                 GroupTypeObjectName, ComplexName, LigandName, OptionsInfo["PocketDistanceCutoff"], Enable=True
  592             )
  593             OutFH.write("%s\n" % PML)
  594         elif re.match("^PocketInorganic$", GroupID, re.I):
  595             OutFH.write(
  596                 """\n""\n"Setting up views for inorganic in pockect around ligand %s in chain %s..."\n""\n"""
  597                 % (LigandID, ChainID)
  598             )
  599             PML = PyMOLUtil.SetupPMLForLigandPocketInorganicView(
  600                 GroupTypeObjectName, ComplexName, LigandName, OptionsInfo["PocketDistanceCutoff"], Enable=True
  601             )
  602             OutFH.write("%s\n" % PML)
  603 
  604         # Set up cryoEM mesh and group...
  605         CryoEMMeshGroupID = "%sCryoEMMeshGroup" % (GroupID)
  606         CryoEMMeshGroupMembersID = "%sCryoEMMeshGroupMembers" % (GroupID)
  607         CryoEMVolumeID = "%sCryoEMVolume" % (GroupID)
  608         CryoEMMeshID = "%sCryoEMMesh" % (GroupID)
  609         CryoEMSurfaceID = "%sCryoEMSurface" % (GroupID)
  610 
  611         CryoEMMapName = PyMOLObjectNames["ComplexCryoEMMap"]
  612         CryoEMVolumeName = PyMOLObjectNames["Ligands"][ChainID][LigandID][CryoEMVolumeID]
  613         CryoEMMeshName = PyMOLObjectNames["Ligands"][ChainID][LigandID][CryoEMMeshID]
  614         CryoEMSurfaceName = PyMOLObjectNames["Ligands"][ChainID][LigandID][CryoEMSurfaceID]
  615         CryoEMMeshGroupName = PyMOLObjectNames["Ligands"][ChainID][LigandID][CryoEMMeshGroupID]
  616         CryoEMMeshGroupMembers = PyMOLObjectNames["Ligands"][ChainID][LigandID][CryoEMMeshGroupMembersID]
  617 
  618         PML = SetupPMLForCryoEMDensityVolume(
  619             CryoEMMapName,
  620             CryoEMVolumeName,
  621             OptionsInfo["VolumeColorRampName"],
  622             Enable=False,
  623             Selection=GroupTypeObjectName,
  624         )
  625         OutFH.write("\n%s\n" % PML)
  626 
  627         ContourLevel = OptionsInfo["MeshLevels"][FileIndex]
  628         PML = SetupPMLForCryoEMDensityMesh(
  629             CryoEMMapName,
  630             CryoEMMeshName,
  631             ContourLevel,
  632             OptionsInfo["MeshColor"],
  633             Enable=True,
  634             Selection=GroupTypeObjectName,
  635         )
  636         OutFH.write("\n%s\n" % PML)
  637 
  638         PML = SetupPMLForCryoEMDensitySurface(
  639             CryoEMMapName,
  640             CryoEMSurfaceName,
  641             ContourLevel,
  642             OptionsInfo["MeshColor"],
  643             Enable=False,
  644             Selection=GroupTypeObjectName,
  645         )
  646         OutFH.write("\n%s\n" % PML)
  647 
  648         GenerateAndWritePMLForGroup(OutFH, CryoEMMeshGroupName, CryoEMMeshGroupMembers, True, "close")
  649 
  650         # Set up polar contacts...
  651         if re.match("^(Pocket|PocketSolvent|PocketInorganic)$", GroupID, re.I):
  652             PolarContactsID = "%sPolarContacts" % (GroupID)
  653             PolarContactsName = PyMOLObjectNames["Ligands"][ChainID][LigandID][PolarContactsID]
  654 
  655             PolarContactsColor = OptionsInfo["PocketContactsLigandColor"]
  656             if re.match("^PocketSolvent$", GroupID, re.I):
  657                 PolarContactsColor = OptionsInfo["PocketContactsSolventColor"]
  658             elif re.match("^PocketInorganic$", GroupID, re.I):
  659                 PolarContactsColor = OptionsInfo["PocketContactsInorganicColor"]
  660 
  661             PML = PyMOLUtil.SetupPMLForPolarContactsView(
  662                 PolarContactsName,
  663                 LigandName,
  664                 GroupTypeObjectName,
  665                 Enable=False,
  666                 Color=PolarContactsColor,
  667                 Cutoff=OptionsInfo["PocketContactsCutoff"],
  668             )
  669             OutFH.write("\n%s\n" % PML)
  670 
  671             OutFH.write("""cmd.set("label_color", "%s", "%s")\n""" % (PolarContactsColor, PolarContactsName))
  672 
  673         # Set up hydrophobic contacts...
  674         if re.match("^Pocket$", GroupID, re.I):
  675             HydrophobicContactsID = "%sHydrophobicContacts" % (GroupID)
  676             HydrophobicContactsName = PyMOLObjectNames["Ligands"][ChainID][LigandID][HydrophobicContactsID]
  677             HydrophobicContactsColor = OptionsInfo["PocketContactsLigandHydrophobicColor"]
  678 
  679             PML = PyMOLUtil.SetupPMLForHydrophobicContactsView(
  680                 HydrophobicContactsName,
  681                 LigandName,
  682                 GroupTypeObjectName,
  683                 Enable=False,
  684                 Color=HydrophobicContactsColor,
  685                 Cutoff=OptionsInfo["PocketContactsCutoff"],
  686             )
  687             OutFH.write("\n%s\n" % PML)
  688             OutFH.write(
  689                 """cmd.set("label_color", "%s", "%s")\n""" % (HydrophobicContactsColor, HydrophobicContactsName)
  690             )
  691 
  692         # Set up hydrophobic surface...
  693         if re.match("^Pocket$", GroupID, re.I) and OptionsInfo["PocketSurface"]:
  694             HydrophobicSurfaceID = "%sHydrophobicSurface" % (GroupID)
  695             HydrophobicSurfaceName = PyMOLObjectNames["Ligands"][ChainID][LigandID][HydrophobicSurfaceID]
  696             PML = PyMOLUtil.SetupPMLForHydrophobicSurfaceView(
  697                 HydrophobicSurfaceName, GroupTypeObjectName, ColorPalette="RedToWhite", Enable=False
  698             )
  699             OutFH.write("\n%s\n" % PML)
  700 
  701             OutFH.write(
  702                 """cmd.set("label_color", "%s", "%s")\n""" % (OptionsInfo["PocketLabelColor"], HydrophobicSurfaceName)
  703             )
  704 
  705         # Setup group....
  706         GroupNameID = "%sGroup" % (GroupID)
  707         GroupMembersID = "%sGroupMembers" % (GroupID)
  708         GroupName = PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupNameID]
  709         GroupMembers = PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID]
  710 
  711         Action = "close"
  712         Enable = False
  713         if re.match("^(Ligand|Pocket)$", GroupID, re.I):
  714             Action = "open"
  715             Enable = True
  716         GenerateAndWritePMLForGroup(OutFH, GroupName, GroupMembers, Enable, Action)
  717 
  718 
  719 def GenerateAndWritePMLForGroup(OutFH, GroupName, GroupMembers, Enable=False, Action="close"):
  720     """Generate and write PML for group."""
  721 
  722     PML = PyMOLUtil.SetupPMLForGroup(GroupName, GroupMembers, Enable, Action)
  723     OutFH.write("""\n""\n"Setting up group %s..."\n""\n""" % GroupName)
  724     OutFH.write("%s\n" % PML)
  725 
  726 
  727 def SetupPMLForCryoEMDensityMap(MapFileName, MapName, AlignMapToObjectName=None, Enable=True):
  728     """Setup PML for loading and viewing cryo-EM density map."""
  729 
  730     PMLCmds = []
  731     PMLCmds.append("""cmd.load("%s", "%s")""" % (MapFileName, MapName))
  732     if AlignMapToObjectName is not None:
  733         PMLCmds.append("""cmd.matrix_copy("%s", "%s")""" % (AlignMapToObjectName, MapName))
  734 
  735     PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(MapName, Enable))
  736 
  737     PML = "\n".join(PMLCmds)
  738 
  739     return PML
  740 
  741 
  742 def SetupPMLForCryoEMDensityMesh(MapName, MeshName, SigmaLevel, Color, Enable=True, Selection=None):
  743     """Setup PML for cryo-EM density mesh."""
  744 
  745     Carve = OptionsInfo["MeshCarveRadius"]
  746 
  747     PMLCmds = []
  748     if Selection is None:
  749         PMLCmds.append("""cmd.isomesh("%s", "%s", %.1f)""" % (MeshName, MapName, SigmaLevel))
  750     else:
  751         PMLCmds.append(
  752             """cmd.isomesh("%s", "%s", %.1f, "(%s)", carve = %.1f)"""
  753             % (MeshName, MapName, SigmaLevel, Selection, Carve)
  754         )
  755     PMLCmds.append(PyMOLUtil.SetupPMLForDeepColoring(MeshName, Color))
  756     PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(MeshName, Enable))
  757 
  758     PML = "\n".join(PMLCmds)
  759 
  760     return PML
  761 
  762 
  763 def SetupPMLForCryoEMDensityVolume(MapName, VolumeName, VolumeColorRamp, Enable=True, Selection=None):
  764     """Setup PML for cryo-EM density volume."""
  765 
  766     Carve = OptionsInfo["VolumeCarveRadius"]
  767 
  768     PMLCmds = []
  769     if Selection is None:
  770         PMLCmds.append("""cmd.volume("%s", "%s", "%s")""" % (VolumeName, MapName, VolumeColorRamp))
  771     else:
  772         PMLCmds.append(
  773             """cmd.volume("%s", "%s", "%s", "(%s)", carve = %.1f)"""
  774             % (VolumeName, MapName, VolumeColorRamp, Selection, Carve)
  775         )
  776     PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(VolumeName, Enable))
  777 
  778     PML = "\n".join(PMLCmds)
  779 
  780     return PML
  781 
  782 
  783 def SetupPMLForCryoEMDensitySurface(MapName, SurfaceName, SigmaLevel, Color, Enable=True, Selection=None):
  784     """Setup PML for cryo-EM density surface."""
  785 
  786     Carve = OptionsInfo["MeshCarveRadius"]
  787 
  788     PMLCmds = []
  789     if Selection is None:
  790         PMLCmds.append("""cmd.isosurface("%s", "%s", %.1f)""" % (SurfaceName, MapName, SigmaLevel))
  791     else:
  792         PMLCmds.append(
  793             """cmd.isosurface("%s", "%s", %.1f, "(%s)", carve = %.1f)"""
  794             % (SurfaceName, MapName, SigmaLevel, Selection, Carve)
  795         )
  796     PMLCmds.append(PyMOLUtil.SetupPMLForDeepColoring(SurfaceName, Color))
  797     PMLCmds.append(PyMOLUtil.SetupPMLForEnableDisable(SurfaceName, Enable))
  798 
  799     PML = "\n".join(PMLCmds)
  800 
  801     return PML
  802 
  803 
  804 def GeneratePyMOLSessionFile():
  805     """Generate PME file from PML file."""
  806 
  807     PSEOutfile = OptionsInfo["PSEOutfile"]
  808     PMLOutfile = OptionsInfo["PMLOutfile"]
  809 
  810     MiscUtil.PrintInfo("\nGenerating file %s..." % PSEOutfile)
  811 
  812     PyMOLUtil.ConvertPMLFileToPSEFile(PMLOutfile, PSEOutfile)
  813 
  814     if not os.path.exists(PSEOutfile):
  815         MiscUtil.PrintWarning("Failed to generate PSE file, %s..." % (PSEOutfile))
  816 
  817     if not OptionsInfo["PMLOut"]:
  818         MiscUtil.PrintInfo("Deleting file %s..." % PMLOutfile)
  819         os.remove(PMLOutfile)
  820 
  821 
  822 def DeleteEmptyPyMOLObjects(OutFH, FileIndex, PyMOLObjectNames):
  823     """Delete empty PyMOL objects."""
  824 
  825     if OptionsInfo["AllowEmptyObjects"]:
  826         return
  827 
  828     SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
  829     for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
  830         OutFH.write("""\n""\n"Checking and deleting empty objects for chain %s..."\n""\n""" % (ChainID))
  831 
  832         # Delete any chain level objects...
  833         WritePMLToCheckAndDeleteEmptyObjects(OutFH, PyMOLObjectNames["Chains"][ChainID]["Solvent"])
  834         WritePMLToCheckAndDeleteEmptyObjects(OutFH, PyMOLObjectNames["Chains"][ChainID]["Inorganic"])
  835 
  836         for LigandID in SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID]:
  837             # Delete ligand level objects...
  838             for GroupID in ["Pocket", "PocketSolvent", "PocketInorganic"]:
  839                 GroupNameID = "%sGroup" % (GroupID)
  840                 GroupName = PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupNameID]
  841 
  842                 GroupTypeObjectID = "%s" % (GroupID)
  843                 GroupTypeObjectName = PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupTypeObjectID]
  844 
  845                 WritePMLToCheckAndDeleteEmptyObjects(OutFH, GroupTypeObjectName, GroupName)
  846 
  847 
  848 def WritePMLToCheckAndDeleteEmptyObjects(OutFH, ObjectName, ParentObjectName=None):
  849     """Write PML to check and delete empty PyMOL objects."""
  850 
  851     if ParentObjectName is None:
  852         PML = """CheckAndDeleteEmptyObjects("%s")""" % (ObjectName)
  853     else:
  854         PML = """CheckAndDeleteEmptyObjects("%s", "%s")""" % (ObjectName, ParentObjectName)
  855 
  856     OutFH.write("%s\n" % PML)
  857 
  858 
  859 def SetupPyMOLObjectNames(FileIndex):
  860     """Setup hierarchy of PyMOL groups and objects for ligand centric views of
  861     cryo-EM density for chains and ligands present in input file.
  862     """
  863 
  864     PyMOLObjectNames = {}
  865     PyMOLObjectNames["Chains"] = {}
  866     PyMOLObjectNames["Ligands"] = {}
  867 
  868     # Setup groups and objects for complex...
  869     SetupPyMOLObjectNamesForComplex(FileIndex, PyMOLObjectNames)
  870 
  871     # Setup groups and objects for chain...
  872     SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
  873     for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
  874         SetupPyMOLObjectNamesForChain(FileIndex, PyMOLObjectNames, ChainID)
  875 
  876         # Setup groups and objects for ligand...
  877         for LigandID in SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID]:
  878             SetupPyMOLObjectNamesForLigand(FileIndex, PyMOLObjectNames, ChainID, LigandID)
  879 
  880     return PyMOLObjectNames
  881 
  882 
  883 def SetupPyMOLObjectNamesForComplex(FileIndex, PyMOLObjectNames):
  884     """Stetup groups and objects for complex."""
  885 
  886     PDBFileRoot = OptionsInfo["InfilesInfo"]["InfilesRoots"][FileIndex]
  887 
  888     PDBGroupName = "%s" % PDBFileRoot
  889     PyMOLObjectNames["PDBGroup"] = PDBGroupName
  890     PyMOLObjectNames["PDBGroupMembers"] = []
  891 
  892     ComplexGroupName = "%s.Complex" % PyMOLObjectNames["PDBGroup"]
  893     PyMOLObjectNames["ComplexGroup"] = ComplexGroupName
  894     PyMOLObjectNames["PDBGroupMembers"].append(ComplexGroupName)
  895 
  896     PyMOLObjectNames["Complex"] = "%s.Complex" % ComplexGroupName
  897 
  898     CryoEMMeshGroupName = "%s.CryoEM" % (ComplexGroupName)
  899     CryoEMMapName = "%s.Map" % (CryoEMMeshGroupName)
  900     CryoEMVolumeName = "%s.Volume" % (CryoEMMeshGroupName)
  901     CryoEMMeshName = "%s.Mesh" % (CryoEMMeshGroupName)
  902     CryoEMSurfaceName = "%s.Surface" % (CryoEMMeshGroupName)
  903 
  904     PyMOLObjectNames["ComplexCryoEMGroup"] = CryoEMMeshGroupName
  905     PyMOLObjectNames["ComplexCryoEMMap"] = CryoEMMapName
  906     PyMOLObjectNames["ComplexCryoEMVolume"] = CryoEMVolumeName
  907     PyMOLObjectNames["ComplexCryoEMMesh"] = CryoEMMeshName
  908     PyMOLObjectNames["ComplexCryoEMSurface"] = CryoEMSurfaceName
  909 
  910     PyMOLObjectNames["ComplexCryoEMGroupMembers"] = []
  911     PyMOLObjectNames["ComplexCryoEMGroupMembers"].append(CryoEMMapName)
  912     if OptionsInfo["VolumeComplex"]:
  913         PyMOLObjectNames["ComplexCryoEMGroupMembers"].append(CryoEMVolumeName)
  914     if OptionsInfo["MeshComplex"]:
  915         PyMOLObjectNames["ComplexCryoEMGroupMembers"].append(CryoEMMeshName)
  916     if OptionsInfo["SurfaceComplex"]:
  917         PyMOLObjectNames["ComplexCryoEMGroupMembers"].append(CryoEMSurfaceName)
  918 
  919     PyMOLObjectNames["ComplexGroupMembers"] = []
  920     PyMOLObjectNames["ComplexGroupMembers"].append(PyMOLObjectNames["Complex"])
  921     PyMOLObjectNames["ComplexGroupMembers"].append(PyMOLObjectNames["ComplexCryoEMGroup"])
  922 
  923 
  924 def SetupPyMOLObjectNamesForChain(FileIndex, PyMOLObjectNames, ChainID):
  925     """Setup groups and objects for chain."""
  926 
  927     PDBGroupName = PyMOLObjectNames["PDBGroup"]
  928 
  929     SpecifiedChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"][FileIndex]
  930     MeshChainComplex = SpecifiedChainsAndLigandsInfo["MeshChainComplex"][ChainID]
  931     VolumeChainComplex = SpecifiedChainsAndLigandsInfo["VolumeChainComplex"][ChainID]
  932     SurfaceChainComplex = SpecifiedChainsAndLigandsInfo["SurfaceChainComplex"][ChainID]
  933 
  934     PyMOLObjectNames["Chains"][ChainID] = {}
  935     PyMOLObjectNames["Ligands"][ChainID] = {}
  936 
  937     # Set up chain group and chain objects...
  938     ChainGroupName = "%s.Chain%s" % (PDBGroupName, ChainID)
  939     PyMOLObjectNames["Chains"][ChainID]["ChainGroup"] = ChainGroupName
  940     PyMOLObjectNames["PDBGroupMembers"].append(ChainGroupName)
  941     PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"] = []
  942 
  943     # Setup chain complex group and objects...
  944     ChainComplexGroupName = "%s.Complex" % (ChainGroupName)
  945     PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroup"] = ChainComplexGroupName
  946     PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(ChainComplexGroupName)
  947 
  948     PyMOLObjectNames["Chains"][ChainID]["ChainComplex"] = "%s.Complex" % (ChainComplexGroupName)
  949 
  950     CryoEMMeshGroupName = "%s.CryoEM" % (ChainComplexGroupName)
  951     CryoEMVolumeName = "%s.Volume" % (CryoEMMeshGroupName)
  952     CryoEMMeshName = "%s.Mesh" % (CryoEMMeshGroupName)
  953     CryoEMSurfaceName = "%s.Surface" % (CryoEMMeshGroupName)
  954 
  955     PyMOLObjectNames["Chains"][ChainID]["ChainComplexCryoEMGroup"] = CryoEMMeshGroupName
  956     PyMOLObjectNames["Chains"][ChainID]["ChainComplexCryoEMVolume"] = CryoEMVolumeName
  957     PyMOLObjectNames["Chains"][ChainID]["ChainComplexCryoEMMesh"] = CryoEMMeshName
  958     PyMOLObjectNames["Chains"][ChainID]["ChainComplexCryoEMSurface"] = CryoEMSurfaceName
  959 
  960     PyMOLObjectNames["Chains"][ChainID]["ChainComplexCryoEMGroupMembers"] = []
  961     if VolumeChainComplex:
  962         PyMOLObjectNames["Chains"][ChainID]["ChainComplexCryoEMGroupMembers"].append(CryoEMVolumeName)
  963     if MeshChainComplex:
  964         PyMOLObjectNames["Chains"][ChainID]["ChainComplexCryoEMGroupMembers"].append(CryoEMMeshName)
  965     if SurfaceChainComplex:
  966         PyMOLObjectNames["Chains"][ChainID]["ChainComplexCryoEMGroupMembers"].append(CryoEMSurfaceName)
  967 
  968     NameIDs = ["ChainComplex"]
  969     if MeshChainComplex or VolumeChainComplex or SurfaceChainComplex:
  970         NameIDs.append("ChainComplexCryoEMGroup")
  971 
  972     PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"] = []
  973     for NameID in NameIDs:
  974         Name = PyMOLObjectNames["Chains"][ChainID][NameID]
  975         PyMOLObjectNames["Chains"][ChainID]["ChainComplexGroupMembers"].append(Name)
  976 
  977     # Setup up a group for individual chains...
  978     ChainAloneGroupName = "%s.Chain" % (ChainGroupName)
  979     PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroup"] = ChainAloneGroupName
  980     PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(ChainAloneGroupName)
  981 
  982     PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"] = []
  983 
  984     Name = "%s.Chain" % (ChainAloneGroupName)
  985     PyMOLObjectNames["Chains"][ChainID]["ChainAlone"] = Name
  986     PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"].append(Name)
  987 
  988     if OptionsInfo["BFactorChainCartoonPutty"]:
  989         Name = "%s.BFactor" % (ChainAloneGroupName)
  990         PyMOLObjectNames["Chains"][ChainID]["ChainAloneBFactorPutty"] = Name
  991         PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"].append(Name)
  992 
  993     if OptionsInfo["ChainSelections"]:
  994         # Setup selections group and its subgroups..
  995         SelectionsGroupName = "%s.Selections" % (ChainAloneGroupName)
  996 
  997         SelectionsGroupIDPrefix = "ChainAloneSelections"
  998         SelectionsGroupID = "%sGroup" % SelectionsGroupIDPrefix
  999 
 1000         # Add selections group to chain alone group...
 1001         PyMOLObjectNames["Chains"][ChainID][SelectionsGroupID] = SelectionsGroupName
 1002         PyMOLObjectNames["Chains"][ChainID]["ChainAloneGroupMembers"].append(SelectionsGroupName)
 1003 
 1004         # Initialize selections group members...
 1005         SelectionsGroupMembersID = "%sGroupMembers" % SelectionsGroupIDPrefix
 1006         PyMOLObjectNames["Chains"][ChainID][SelectionsGroupMembersID] = []
 1007 
 1008         # Setup selections name sub group and its members...
 1009         for SelectionName in OptionsInfo["ChainSelectionsInfo"]["Names"]:
 1010             SelectionNameGroupID = SelectionName
 1011 
 1012             SelectionsNameGroupName = "%s.%s" % (SelectionsGroupName, SelectionName)
 1013             SelectionsNameGroupID = "%s%sGroup" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
 1014 
 1015             # Add selections name sub group to selections group...
 1016             PyMOLObjectNames["Chains"][ChainID][SelectionsNameGroupID] = SelectionsNameGroupName
 1017             PyMOLObjectNames["Chains"][ChainID][SelectionsGroupMembersID].append(SelectionsNameGroupName)
 1018 
 1019             # Initialize selections names sub group members...
 1020             SelectionsNameGroupMembersID = "%s%sGroupMembers" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
 1021             PyMOLObjectNames["Chains"][ChainID][SelectionsNameGroupMembersID] = []
 1022 
 1023             # Add selection object to selections name group...
 1024             SelectionObjectName = "%s.Selection" % (SelectionsNameGroupName)
 1025             SelectionObjectID = "%s%sSelection" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
 1026 
 1027             PyMOLObjectNames["Chains"][ChainID][SelectionObjectID] = SelectionObjectName
 1028             PyMOLObjectNames["Chains"][ChainID][SelectionsNameGroupMembersID].append(SelectionObjectName)
 1029 
 1030             # Setup cryo-EM mesh group and add it to selections name group...
 1031             CryoEMMeshGroupName = "%s.CryoEM" % (SelectionsNameGroupName)
 1032             CryoEMMeshGroupID = "%s%sCryoEMGroup" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
 1033 
 1034             PyMOLObjectNames["Chains"][ChainID][CryoEMMeshGroupID] = CryoEMMeshGroupName
 1035             PyMOLObjectNames["Chains"][ChainID][SelectionsNameGroupMembersID].append(CryoEMMeshGroupName)
 1036 
 1037             # Initialize cryo-EM mesh group members...
 1038             CryoEMMeshGroupMembersID = "%s%sCryoEMGroupMembers" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
 1039             PyMOLObjectNames["Chains"][ChainID][CryoEMMeshGroupMembersID] = []
 1040 
 1041             # Setup members of cryo-EM mesh group...
 1042             CryoEMVolumeName = "%s.Volume" % (CryoEMMeshGroupName)
 1043             CryoEMVolumeID = "%s%sCryoEMVolume" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
 1044             CryoEMMeshName = "%s.Mesh" % (CryoEMMeshGroupName)
 1045             CryoEMMeshID = "%s%sCryoEMMesh" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
 1046             CryoEMSurfaceName = "%s.Surface" % (CryoEMMeshGroupName)
 1047             CryoEMSurfaceID = "%s%sCryoEMSurface" % (SelectionsGroupIDPrefix, SelectionNameGroupID)
 1048 
 1049             PyMOLObjectNames["Chains"][ChainID][CryoEMVolumeID] = CryoEMVolumeName
 1050             PyMOLObjectNames["Chains"][ChainID][CryoEMMeshID] = CryoEMMeshName
 1051             PyMOLObjectNames["Chains"][ChainID][CryoEMSurfaceID] = CryoEMSurfaceName
 1052 
 1053             PyMOLObjectNames["Chains"][ChainID][CryoEMMeshGroupMembersID].append(CryoEMVolumeName)
 1054             PyMOLObjectNames["Chains"][ChainID][CryoEMMeshGroupMembersID].append(CryoEMMeshName)
 1055             PyMOLObjectNames["Chains"][ChainID][CryoEMMeshGroupMembersID].append(CryoEMSurfaceName)
 1056 
 1057     # Setup solvent and inorganic objects for chain...
 1058     for NameID in ["Solvent", "Inorganic"]:
 1059         Name = "%s.%s" % (ChainGroupName, NameID)
 1060         PyMOLObjectNames["Chains"][ChainID][NameID] = Name
 1061         PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(Name)
 1062 
 1063 
 1064 def SetupPyMOLObjectNamesForLigand(FileIndex, PyMOLObjectNames, ChainID, LigandID):
 1065     """Stetup groups and objects for ligand."""
 1066 
 1067     PyMOLObjectNames["Ligands"][ChainID][LigandID] = {}
 1068 
 1069     ChainGroupName = PyMOLObjectNames["Chains"][ChainID]["ChainGroup"]
 1070 
 1071     # Setup a chain level ligand group...
 1072     ChainLigandGroupName = "%s.Ligand%s" % (ChainGroupName, LigandID)
 1073     PyMOLObjectNames["Ligands"][ChainID][LigandID]["ChainLigandGroup"] = ChainLigandGroupName
 1074     PyMOLObjectNames["Chains"][ChainID]["ChainGroupMembers"].append(ChainLigandGroupName)
 1075 
 1076     PyMOLObjectNames["Ligands"][ChainID][LigandID]["ChainLigandGroupMembers"] = []
 1077 
 1078     # Set up groups and objects for a specific ligand group...
 1079     for GroupType in ["Ligand", "Pocket", "Pocket_Solvent", "Pocket_Inorganic"]:
 1080         GroupID = re.sub("_", "", GroupType)
 1081         GroupName = "%s.%s" % (ChainLigandGroupName, GroupType)
 1082 
 1083         GroupNameID = "%sGroup" % (GroupID)
 1084         GroupMembersID = "%sGroupMembers" % (GroupID)
 1085 
 1086         PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupNameID] = GroupName
 1087         PyMOLObjectNames["Ligands"][ChainID][LigandID]["ChainLigandGroupMembers"].append(GroupName)
 1088 
 1089         GroupTypeObjectName = "%s.%s" % (GroupName, GroupType)
 1090         GroupTypeObjectID = "%s" % (GroupID)
 1091         PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupTypeObjectID] = GroupTypeObjectName
 1092 
 1093         CryoEMMeshGroupName = "%s.CryoEM" % (GroupName)
 1094         CryoEMVolumeName = "%s.Volume" % (CryoEMMeshGroupName)
 1095         CryoEMMeshName = "%s.Mesh" % (CryoEMMeshGroupName)
 1096         CryoEMSurfaceName = "%s.Surface" % (CryoEMMeshGroupName)
 1097 
 1098         CryoEMMeshGroupID = "%sCryoEMMeshGroup" % (GroupID)
 1099         CryoEMMeshGroupMembersID = "%sCryoEMMeshGroupMembers" % (GroupID)
 1100         CryoEMVolumeID = "%sCryoEMVolume" % (GroupID)
 1101         CryoEMMeshID = "%sCryoEMMesh" % (GroupID)
 1102         CryoEMSurfaceID = "%sCryoEMSurface" % (GroupID)
 1103 
 1104         PyMOLObjectNames["Ligands"][ChainID][LigandID][CryoEMMeshGroupID] = CryoEMMeshGroupName
 1105         PyMOLObjectNames["Ligands"][ChainID][LigandID][CryoEMVolumeID] = CryoEMVolumeName
 1106         PyMOLObjectNames["Ligands"][ChainID][LigandID][CryoEMMeshID] = CryoEMMeshName
 1107         PyMOLObjectNames["Ligands"][ChainID][LigandID][CryoEMSurfaceID] = CryoEMSurfaceName
 1108         PyMOLObjectNames["Ligands"][ChainID][LigandID][CryoEMMeshGroupMembersID] = []
 1109         PyMOLObjectNames["Ligands"][ChainID][LigandID][CryoEMMeshGroupMembersID].append(CryoEMVolumeName)
 1110         PyMOLObjectNames["Ligands"][ChainID][LigandID][CryoEMMeshGroupMembersID].append(CryoEMMeshName)
 1111         PyMOLObjectNames["Ligands"][ChainID][LigandID][CryoEMMeshGroupMembersID].append(CryoEMSurfaceName)
 1112 
 1113         PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID] = []
 1114         NameIDs = [GroupTypeObjectID, CryoEMMeshGroupID]
 1115 
 1116         for NameID in NameIDs:
 1117             Name = PyMOLObjectNames["Ligands"][ChainID][LigandID][NameID]
 1118             PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID].append(Name)
 1119 
 1120         if re.match("^Ligand$", GroupType, re.I):
 1121             # No other object needed for Ligand group...
 1122             continue
 1123 
 1124         PolarContactsName = "%s.Polar_Contacts" % (GroupName)
 1125         PolarContactsID = "%sPolarContacts" % (GroupID)
 1126         PyMOLObjectNames["Ligands"][ChainID][LigandID][PolarContactsID] = PolarContactsName
 1127         PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID].append(PolarContactsName)
 1128 
 1129         if not re.match("^Pocket$", GroupType, re.I):
 1130             # No other object needed for any other group besides Pocket...
 1131             continue
 1132 
 1133         if not OptionsInfo["PocketSurface"]:
 1134             continue
 1135 
 1136         HydrophobicContactsName = "%s.Hydrophobic_Contacts" % (GroupName)
 1137         HydrophobicContactsID = "%sHydrophobicContacts" % (GroupID)
 1138         PyMOLObjectNames["Ligands"][ChainID][LigandID][HydrophobicContactsID] = HydrophobicContactsName
 1139         PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID].append(HydrophobicContactsName)
 1140 
 1141         HydrophobicSurfaceName = "%s.Surface" % (GroupName)
 1142         HydrophobicSurfaceID = "%sHydrophobicSurface" % (GroupID)
 1143         PyMOLObjectNames["Ligands"][ChainID][LigandID][HydrophobicSurfaceID] = HydrophobicSurfaceName
 1144         PyMOLObjectNames["Ligands"][ChainID][LigandID][GroupMembersID].append(HydrophobicSurfaceName)
 1145 
 1146 
 1147 def ProcessDensityMapFiles():
 1148     """Process density map files."""
 1149 
 1150     DensityMapFiles = OptionsInfo["DensityMapFiles"]
 1151     if re.match("^auto$", DensityMapFiles, re.I):
 1152         ProcessAutoDensityMapFiles()
 1153     else:
 1154         ProcessSpecifiedDensityMapFiles()
 1155 
 1156 
 1157 def ProcessSpecifiedDensityMapFiles():
 1158     """Process specified density map files."""
 1159 
 1160     OptionsInfo["DensityMapFilesNames"] = []
 1161 
 1162     MiscUtil.PrintInfo("\nProcessing cryo-EM density file names...")
 1163 
 1164     DensityMapFiles = re.sub(" ", "", OptionsInfo["DensityMapFiles"])
 1165     if not DensityMapFiles:
 1166         MiscUtil.PrintError('No valid value specified using "--densityMapFiles" option.')
 1167 
 1168     DensityMapFilesWords = DensityMapFiles.split(",")
 1169     DensityMapFilesWordsCount = len(DensityMapFilesWords)
 1170 
 1171     InfilesNamesCount = len(OptionsInfo["InfilesNames"])
 1172 
 1173     if DensityMapFilesWordsCount != InfilesNamesCount:
 1174         MiscUtil.PrintError(
 1175             'The number of comma delimited cryo-EM density map files, %d, specified using "--DensityMapFiles" must be equal to the number of input files, %s, specified using "-i, --infiles" option.'
 1176             % (DensityMapFilesWordsCount, InfilesNamesCount)
 1177         )
 1178 
 1179     for Index in range(0, InfilesNamesCount):
 1180         Infile = OptionsInfo["InfilesNames"][Index]
 1181         DensityMapFile = DensityMapFilesWords[Index]
 1182 
 1183         if not os.path.exists(DensityMapFile):
 1184             MiscUtil.PrintError(
 1185                 'The cryo-EM density ED map file, %s, specified using option "--DensityMapFiles", corresponding to input file, %s,  doesn\'t exist.\n'
 1186                 % (DensityMapFile, Infile)
 1187             )
 1188 
 1189         OptionsInfo["DensityMapFilesNames"].append(DensityMapFile)
 1190 
 1191 
 1192 def ProcessAutoDensityMapFiles():
 1193     """Set up and process name of density map files."""
 1194 
 1195     OptionsInfo["DensityMapFilesNames"] = []
 1196     InfilesNamesCount = len(OptionsInfo["InfilesNames"])
 1197 
 1198     MiscUtil.PrintInfo("\nSetting cryo-EM density file names...")
 1199 
 1200     for Index in range(0, InfilesNamesCount):
 1201         Infile = OptionsInfo["InfilesNames"][Index]
 1202 
 1203         EMDBID = RetrieveEMDBID(Infile)
 1204         if EMDBID is None:
 1205             MiscUtil.PrintError(
 1206                 'Failed to retrieve EMDB ID from input file %s to automatically set density map file name. Use option "-d, --densityMapFiles " to specify density map file name and try again.'
 1207                 % Infile
 1208             )
 1209 
 1210         DensityMapFile = None
 1211         MapFileRoot = "emd_%s" % EMDBID
 1212         MapFile1 = "%s.map.gz" % MapFileRoot
 1213         MapFile2 = "%s.map" % MapFileRoot
 1214         if os.path.exists(MapFile1):
 1215             DensityMapFile = MapFile1
 1216         elif os.path.exists(MapFile2):
 1217             DensityMapFile = MapFile2
 1218         else:
 1219             MiscUtil.PrintError(
 1220                 'Density map files %s or %s don\'t exist. Use option "-d, --densityMapFiles " to specify density map file name and try again'
 1221                 % (MapFile1, MapFile2)
 1222             )
 1223 
 1224         MiscUtil.PrintInfo("Setting density map file name as %s for input file %s..." % (DensityMapFile, Infile))
 1225         OptionsInfo["DensityMapFilesNames"].append(DensityMapFile)
 1226 
 1227 
 1228 def RetrieveRecommededContourLevel(Infile):
 1229     """Retrieve recommened contour level."""
 1230 
 1231     if Infile in OptionsInfo["InfilesRecommededContourLevels"]:
 1232         RecommendedContourLevel = OptionsInfo["InfilesRecommededContourLevels"][Infile]
 1233         return RecommendedContourLevel
 1234 
 1235     RecommendedContourLevel = None
 1236     EMDBID = RetrieveEMDBID(Infile)
 1237     if EMDBID is None:
 1238         MiscUtil.PrintWarning(
 1239             "Failed to retrieve EMDB ID from input file %s to detect local header file already downloaded from EMDB server..."
 1240             % Infile
 1241         )
 1242         OptionsInfo["InfilesRecommededContourLevels"][Infile] = RecommendedContourLevel
 1243         return RecommendedContourLevel
 1244 
 1245     MetadataHeaderFile = "emd-%s.xml" % (EMDBID)
 1246     if not os.path.exists(MetadataHeaderFile):
 1247         MiscUtil.PrintWarning(
 1248             "Failed to find a local header file, %s, for EMDB ID %s..." % (MetadataHeaderFile, EMDBID)
 1249         )
 1250         OptionsInfo["InfilesRecommededContourLevels"][Infile] = RecommendedContourLevel
 1251         return RecommendedContourLevel
 1252 
 1253     MiscUtil.PrintInfo(
 1254         "\nRetrieving recommeded contour level from header file, %s, for input file, %s..."
 1255         % (MetadataHeaderFile, Infile)
 1256     )
 1257 
 1258     ContourLevel = None
 1259     Source = None
 1260     XMLTree = ElementTree.parse(MetadataHeaderFile)
 1261 
 1262     MapElement = XMLTree.find("map")
 1263     if MapElement is not None:
 1264         ContourLevelElement = MapElement.find("contourLevel")
 1265         if ContourLevelElement is not None:
 1266             ContourLevel = ContourLevelElement.text
 1267             Source = ContourLevelElement.get("source")
 1268 
 1269     if ContourLevel is not None:
 1270         if Source is None:
 1271             Source = "NA"
 1272         MiscUtil.PrintInfo("Retrieved recommended (Source: %s) contour level %s..." % (Source, ContourLevel))
 1273         RecommendedContourLevel = ContourLevel
 1274 
 1275     OptionsInfo["InfilesRecommededContourLevels"][Infile] = RecommendedContourLevel
 1276 
 1277     return RecommendedContourLevel
 1278 
 1279 
 1280 def RetrieveEMDBID(Infile):
 1281     """Retrieve EMDB ID from input file."""
 1282 
 1283     if Infile in OptionsInfo["InfilesEMDBIDs"]:
 1284         EMDBID = OptionsInfo["InfilesEMDBIDs"][Infile]
 1285         return EMDBID
 1286 
 1287     EMDBID = None
 1288     FileDir, FileName, FileExt = MiscUtil.ParseFileName(Infile)
 1289 
 1290     if re.match("^pdb$", FileExt, re.I):
 1291         EMDBID = RetriveEMDBIDFromPDBFile(Infile)
 1292     elif re.match("^cif$", FileExt, re.I):
 1293         EMDBID = RetriveEMDBIDFromCIFFile(Infile)
 1294     else:
 1295         EMDBID = None
 1296 
 1297     OptionsInfo["InfilesEMDBIDs"][Infile] = EMDBID
 1298 
 1299     return EMDBID
 1300 
 1301 
 1302 def RetriveEMDBIDFromPDBFile(Infile):
 1303     """Retrieve EMDB ID from PDB file."""
 1304 
 1305     EMDBID = None
 1306     InfileFH = open(Infile, "r")
 1307     if InfileFH is None:
 1308         MiscUtil.PrintError("Couldn't open input file: %s.\n" % (Infile))
 1309 
 1310     MiscUtil.PrintInfo("\nRetrieving EMDB ID from input file %s..." % Infile)
 1311 
 1312     EMDBID = None
 1313     for Line in InfileFH:
 1314         Line = Line.rstrip()
 1315         if re.match("^REMARK", Line, re.I):
 1316             if re.search("DB: EMDB", Line, re.I):
 1317                 for Word in Line.split(" "):
 1318                     # Retrieve string with EMD-
 1319                     if re.search("EMD-", Word, re.I):
 1320                         Word = Word.strip()
 1321                         EMDBID = re.sub("EMD-", "", Word)
 1322                         break
 1323                 break
 1324     InfileFH.close()
 1325 
 1326     return EMDBID
 1327 
 1328 
 1329 def RetriveEMDBIDFromCIFFile(Infile):
 1330     """Retrieve EMDB ID from CIF file."""
 1331 
 1332     InfileFH = open(Infile, "r")
 1333     if InfileFH is None:
 1334         MiscUtil.PrintError("Couldn't open input file: %s.\n" % (Infile))
 1335 
 1336     MiscUtil.PrintInfo("\nRetrieving EMDB ID from input file %s..." % Infile)
 1337 
 1338     EMDBID = None
 1339     for Line in InfileFH:
 1340         Line = Line.rstrip()
 1341         if re.match("^EMDB  EMD", Line, re.I):
 1342             for Word in Line.split(" "):
 1343                 # Retrieve string with EMD-
 1344                 if re.search("EMD-", Word, re.I):
 1345                     Word = Word.strip()
 1346                     EMDBID = re.sub("EMD-", "", Word)
 1347                     break
 1348             break
 1349     InfileFH.close()
 1350 
 1351     return EMDBID
 1352 
 1353 
 1354 def RetrieveInfilesInfo():
 1355     """Retrieve information for input files."""
 1356 
 1357     InfilesInfo = {}
 1358 
 1359     InfilesInfo["InfilesNames"] = []
 1360     InfilesInfo["InfilesRoots"] = []
 1361     InfilesInfo["ChainsAndLigandsInfo"] = []
 1362 
 1363     for Infile in OptionsInfo["InfilesNames"]:
 1364         FileDir, FileName, FileExt = MiscUtil.ParseFileName(Infile)
 1365         InfileRoot = FileName
 1366 
 1367         ChainsAndLigandInfo = PyMOLUtil.GetChainsAndLigandsInfo(Infile, InfileRoot)
 1368 
 1369         InfilesInfo["InfilesNames"].append(Infile)
 1370         InfilesInfo["InfilesRoots"].append(InfileRoot)
 1371         InfilesInfo["ChainsAndLigandsInfo"].append(ChainsAndLigandInfo)
 1372 
 1373     OptionsInfo["InfilesInfo"] = InfilesInfo
 1374 
 1375 
 1376 def RetrieveRefFileInfo():
 1377     """Retrieve information for ref file."""
 1378 
 1379     RefFileInfo = {}
 1380     if not OptionsInfo["Align"]:
 1381         OptionsInfo["RefFileInfo"] = RefFileInfo
 1382         return
 1383 
 1384     RefFile = OptionsInfo["RefFileName"]
 1385 
 1386     FileDir, FileName, FileExt = MiscUtil.ParseFileName(RefFile)
 1387     RefFileRoot = FileName
 1388 
 1389     if re.match("^FirstInputFile$", OptionsInfo["AlignRefFile"], re.I):
 1390         ChainsAndLigandInfo = OptionsInfo["InfilesInfo"]["ChainsAndLigandsInfo"][0]
 1391     else:
 1392         MiscUtil.PrintInfo("\nRetrieving chain and ligand information for alignment reference file %s..." % RefFile)
 1393         ChainsAndLigandInfo = PyMOLUtil.GetChainsAndLigandsInfo(RefFile, RefFileRoot)
 1394 
 1395     RefFileInfo["RefFileName"] = RefFile
 1396     RefFileInfo["RefFileRoot"] = RefFileRoot
 1397     RefFileInfo["PyMOLObjectName"] = "AlignRef_%s" % RefFileRoot
 1398     RefFileInfo["ChainsAndLigandsInfo"] = ChainsAndLigandInfo
 1399 
 1400     OptionsInfo["RefFileInfo"] = RefFileInfo
 1401 
 1402 
 1403 def ProcessChainAndLigandIDs():
 1404     """Process specified chain and ligand IDs for infiles."""
 1405 
 1406     OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"] = []
 1407 
 1408     for FileIndex in range(0, len(OptionsInfo["InfilesInfo"]["InfilesNames"])):
 1409         MiscUtil.PrintInfo(
 1410             "\nProcessing specified chain and ligand IDs for input file %s..."
 1411             % OptionsInfo["InfilesInfo"]["InfilesNames"][FileIndex]
 1412         )
 1413 
 1414         ChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["ChainsAndLigandsInfo"][FileIndex]
 1415         SpecifiedChainsAndLigandsInfo = PyMOLUtil.ProcessChainsAndLigandsOptionsInfo(
 1416             ChainsAndLigandsInfo, "-c, --chainIDs", OptionsInfo["ChainIDs"], "-l, --ligandIDs", OptionsInfo["LigandIDs"]
 1417         )
 1418         ProcessChainMeshesVolumesAndSurfacesOptions(SpecifiedChainsAndLigandsInfo)
 1419         OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"].append(SpecifiedChainsAndLigandsInfo)
 1420 
 1421         CheckPresenceOfValidLigandIDs(ChainsAndLigandsInfo, SpecifiedChainsAndLigandsInfo)
 1422 
 1423 
 1424 def CheckPresenceOfValidLigandIDs(ChainsAndLigandsInfo, SpecifiedChainsAndLigandsInfo):
 1425     """Check presence of valid ligand IDs."""
 1426 
 1427     MiscUtil.PrintInfo("\nSpecified chain IDs: %s" % (", ".join(SpecifiedChainsAndLigandsInfo["ChainIDs"])))
 1428 
 1429     for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
 1430         if len(SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID]):
 1431             MiscUtil.PrintInfo(
 1432                 "Chain ID: %s; Specified LigandIDs: %s"
 1433                 % (ChainID, ", ".join(SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID]))
 1434             )
 1435         else:
 1436             MiscUtil.PrintInfo("Chain IDs: %s; Specified LigandIDs: None" % (ChainID))
 1437             MiscUtil.PrintWarning(
 1438                 "No valid ligand IDs found for chain ID, %s. PyMOL groups and objects related to ligand and binding pockect won't be created."
 1439                 % (ChainID)
 1440             )
 1441 
 1442 
 1443 def RetrieveFirstChainID(FileIndex):
 1444     """Get first chain ID."""
 1445 
 1446     ChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["ChainsAndLigandsInfo"][FileIndex]
 1447 
 1448     FirstChainID = None
 1449     if len(ChainsAndLigandsInfo["ChainIDs"]):
 1450         FirstChainID = ChainsAndLigandsInfo["ChainIDs"][0]
 1451 
 1452     return FirstChainID
 1453 
 1454 
 1455 def ProcessChainMeshesVolumesAndSurfacesOptions(SpecifiedChainsAndLigandsInfo):
 1456     """Process options to create meshes and surfaces for chains."""
 1457 
 1458     SpecifiedChainsAndLigandsInfo["VolumeChainComplex"] = {}
 1459     SpecifiedChainsAndLigandsInfo["MeshChainComplex"] = {}
 1460     SpecifiedChainsAndLigandsInfo["SurfaceChainComplex"] = {}
 1461 
 1462     SpecifiedChainsAndLigandsInfo["EnableVolumeChainComplex"] = {}
 1463     SpecifiedChainsAndLigandsInfo["EnableMeshChainComplex"] = {}
 1464     SpecifiedChainsAndLigandsInfo["EnableSurfaceChainComplex"] = {}
 1465 
 1466     SpecifiedChainsAndLigandsInfo["EnableChainComplexGroup"] = {}
 1467     SpecifiedChainsAndLigandsInfo["EnableChainAloneGroup"] = {}
 1468 
 1469     for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
 1470         LigandsPresent = True if len(SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID]) else False
 1471 
 1472         # Create and enable mesh  or volume in auto mode...
 1473         if re.match("^auto$", OptionsInfo["MeshChainComplex"], re.I):
 1474             MeshChainComplex = False if LigandsPresent else True
 1475             EnableMeshChainComplex = False if LigandsPresent else True
 1476         else:
 1477             MeshChainComplex = True if re.match("^Yes$", OptionsInfo["MeshChainComplex"], re.I) else False
 1478             EnableMeshChainComplex = True if re.match("^Yes$", OptionsInfo["MeshChainComplex"], re.I) else False
 1479 
 1480         if re.match("^auto$", OptionsInfo["VolumeChainComplex"], re.I):
 1481             VolumeChainComplex = False if LigandsPresent else True
 1482             EnableVolumeChainComplex = False if LigandsPresent else True
 1483         else:
 1484             VolumeChainComplex = True if re.match("^Yes$", OptionsInfo["VolumeChainComplex"], re.I) else False
 1485             EnableVolumeChainComplex = True if re.match("^Yes$", OptionsInfo["VolumeChainComplex"], re.I) else False
 1486 
 1487         if MeshChainComplex and EnableMeshChainComplex:
 1488             EnableVolumeChainComplex = False
 1489 
 1490         # Create and enable surface in auto mode based on the status of mesh and volume...
 1491         if re.match("^auto$", OptionsInfo["SurfaceChainComplex"], re.I):
 1492             SurfaceChainComplex = False if LigandsPresent else True
 1493             EnableSurfaceChainComplex = False if LigandsPresent else True
 1494 
 1495             if MeshChainComplex or VolumeChainComplex:
 1496                 SurfaceChainComplex = False
 1497                 EnableSurfaceChainComplex = False
 1498         else:
 1499             SurfaceChainComplex = True if re.match("^Yes$", OptionsInfo["SurfaceChainComplex"], re.I) else False
 1500             EnableSurfaceChainComplex = True if re.match("^Yes$", OptionsInfo["SurfaceChainComplex"], re.I) else False
 1501 
 1502         if (MeshChainComplex and EnableMeshChainComplex) or (VolumeChainComplex or EnableVolumeChainComplex):
 1503             EnableSurfaceChainComplex = False
 1504 
 1505         if LigandsPresent:
 1506             EnableChainComplexGroup = False
 1507             EnableChainAloneGroup = True
 1508         else:
 1509             EnableChainComplexGroup = True
 1510             EnableChainAloneGroup = False
 1511 
 1512         SpecifiedChainsAndLigandsInfo["VolumeChainComplex"][ChainID] = VolumeChainComplex
 1513         SpecifiedChainsAndLigandsInfo["MeshChainComplex"][ChainID] = MeshChainComplex
 1514         SpecifiedChainsAndLigandsInfo["SurfaceChainComplex"][ChainID] = SurfaceChainComplex
 1515 
 1516         SpecifiedChainsAndLigandsInfo["EnableVolumeChainComplex"][ChainID] = EnableVolumeChainComplex
 1517         SpecifiedChainsAndLigandsInfo["EnableMeshChainComplex"][ChainID] = EnableMeshChainComplex
 1518         SpecifiedChainsAndLigandsInfo["EnableSurfaceChainComplex"][ChainID] = EnableSurfaceChainComplex
 1519 
 1520         SpecifiedChainsAndLigandsInfo["EnableChainComplexGroup"][ChainID] = EnableChainComplexGroup
 1521         SpecifiedChainsAndLigandsInfo["EnableChainAloneGroup"][ChainID] = EnableChainAloneGroup
 1522 
 1523 
 1524 def ProcessChainSelections():
 1525     """Process custom selections for chains."""
 1526 
 1527     ChainSelectionsInfo = PyMOLUtil.ProcessChainSelectionsOptionsInfo(
 1528         "--selectionsChain", OptionsInfo["SelectionsChain"]
 1529     )
 1530     OptionsInfo["ChainSelectionsInfo"] = ChainSelectionsInfo
 1531 
 1532     ChainSelections = True if len(OptionsInfo["ChainSelectionsInfo"]["Names"]) else False
 1533     OptionsInfo["ChainSelections"] = ChainSelections
 1534 
 1535 
 1536 def ProcessMeshLevel():
 1537     """Process mesh level."""
 1538 
 1539     MeshLevel = OptionsInfo["MeshLevel"]
 1540 
 1541     if re.match("^auto$", MeshLevel, re.I):
 1542         ProcessAutoMeshLevel()
 1543     else:
 1544         ProcessSpecifiedMeshLevel()
 1545 
 1546 
 1547 def ProcessAutoMeshLevel():
 1548     """Process auto mesh level."""
 1549 
 1550     OptionsInfo["MeshLevels"] = []
 1551     InfilesNamesCount = len(OptionsInfo["InfilesNames"])
 1552 
 1553     MiscUtil.PrintInfo("\nSetting mesh levels...")
 1554 
 1555     for Index in range(0, InfilesNamesCount):
 1556         Infile = OptionsInfo["InfilesNames"][Index]
 1557 
 1558         RecommededContourLevel = RetrieveRecommededContourLevel(Infile)
 1559         if RecommededContourLevel is None:
 1560             MiscUtil.PrintWarning(
 1561                 'Failed to retrieve recommended mesh contour level from header. It\'s being set to 1.0. Use "--meshLevel" option to specify a different contour mesh level.'
 1562             )
 1563             MeshLevel = 1.0
 1564         else:
 1565             MeshLevel = float(RecommededContourLevel)
 1566         OptionsInfo["MeshLevels"].append(MeshLevel)
 1567 
 1568 
 1569 def ProcessSpecifiedMeshLevel():
 1570     """Process specified mesh level."""
 1571 
 1572     MiscUtil.PrintInfo("\nProcessing mesh levels...")
 1573 
 1574     OptionsInfo["MeshLevels"] = []
 1575     InfilesNamesCount = len(OptionsInfo["InfilesNames"])
 1576 
 1577     MeshLevels = re.sub(" ", "", OptionsInfo["MeshLevel"])
 1578     if not MeshLevels:
 1579         MiscUtil.PrintError('No valid value specified using "--meshLevel" option.')
 1580 
 1581     MeshLevelWords = MeshLevels.split(",")
 1582     MeshLevelWordsCount = len(MeshLevelWords)
 1583 
 1584     if MeshLevelWordsCount != InfilesNamesCount:
 1585         MiscUtil.PrintError(
 1586             'The number of comma delimited mesh levels, %d, specified using "--meshLevel" must be equal to the number of input files, %s, specified using "-i, --infiles" option.'
 1587             % (MeshLevelWordsCount, InfilesNamesCount)
 1588         )
 1589 
 1590     for Index in range(0, InfilesNamesCount):
 1591         Infile = OptionsInfo["InfilesNames"][Index]
 1592         MeshLevel = MeshLevelWords[Index]
 1593         if not MiscUtil.IsFloat(MeshLevel):
 1594             MiscUtil.PrintError(
 1595                 'The mesh level, %s, specified using "--meshLevel" for input file, %s, must be a float.'
 1596                 % (MeshLevel, Infile)
 1597             )
 1598 
 1599         MeshLevel = float(MeshLevel)
 1600         OptionsInfo["MeshLevels"].append(MeshLevel)
 1601 
 1602 
 1603 def ProcessVolumeColorRamp():
 1604     """Process volume color ramp."""
 1605 
 1606     MiscUtil.PrintInfo("\nProcessing volume color ramp...")
 1607 
 1608     ColorRamp = Options["--volumeColorRamp"]
 1609     ColorRampName = None
 1610     CreateColorRamp = False
 1611     ColorRampContourLevel = None
 1612     if re.match("^auto$", ColorRamp, re.I):
 1613         FirstInfile = OptionsInfo["InfilesNames"][0]
 1614         RecommededContourLevel = RetrieveRecommededContourLevel(FirstInfile)
 1615         if RecommededContourLevel is None:
 1616             ColorRampName = "default"
 1617             MiscUtil.PrintWarning(
 1618                 "Failed to retrieve recommended contour level from header file corresponding to input file, %s, to create a new ramp. Using PyMOL default volume color ramp."
 1619                 % (FirstInfile)
 1620             )
 1621         else:
 1622             ColorRampName = "CryoEMAuto"
 1623             CreateColorRamp = True
 1624             ColorRampContourLevel = float(RecommededContourLevel)
 1625             MiscUtil.PrintInfo(
 1626                 "\nSetting up a  new volume color ramp, %s, at recommended contour level, %.2f, from input file, %s..."
 1627                 % (ColorRampName, ColorRampContourLevel, FirstInfile)
 1628             )
 1629     else:
 1630         ColorRampName = ColorRamp
 1631 
 1632     OptionsInfo["VolumeColorRamp"] = ColorRamp
 1633     OptionsInfo["VolumeColorRampName"] = ColorRampName
 1634     OptionsInfo["VolumeColorRampCreate"] = CreateColorRamp
 1635     OptionsInfo["VolumeColorRampContourLevel"] = ColorRampContourLevel
 1636 
 1637 
 1638 def ProcessOptions():
 1639     """Process and validate command line arguments and options."""
 1640 
 1641     MiscUtil.PrintInfo("Processing options...")
 1642 
 1643     # Validate options...
 1644     ValidateOptions()
 1645 
 1646     OptionsInfo["Align"] = True if re.match("^Yes$", Options["--align"], re.I) else False
 1647     OptionsInfo["AlignMethod"] = Options["--alignMethod"].lower()
 1648     OptionsInfo["AlignMode"] = Options["--alignMode"]
 1649 
 1650     OptionsInfo["AllowEmptyObjects"] = True if re.match("^Yes$", Options["--allowEmptyObjects"], re.I) else False
 1651 
 1652     OptionsInfo["BFactorChainCartoonPutty"] = (
 1653         True if re.match("^Yes$", Options["--BFactorChainCartoonPutty"], re.I) else False
 1654     )
 1655     OptionsInfo["BFactorColorPalette"] = Options["--BFactorColorPalette"]
 1656 
 1657     OptionsInfo["Infiles"] = Options["--infiles"]
 1658     OptionsInfo["InfilesNames"] = Options["--infileNames"]
 1659 
 1660     OptionsInfo["InfilesEMDBIDs"] = {}
 1661     OptionsInfo["InfilesRecommededContourLevels"] = {}
 1662 
 1663     OptionsInfo["AlignRefFile"] = Options["--alignRefFile"]
 1664     if re.match("^FirstInputFile$", Options["--alignRefFile"], re.I):
 1665         OptionsInfo["RefFileName"] = OptionsInfo["InfilesNames"][0]
 1666     else:
 1667         OptionsInfo["RefFileName"] = Options["--alignRefFile"]
 1668 
 1669     OptionsInfo["IgnoreHydrogens"] = True if re.match("^Yes$", Options["--ignoreHydrogens"], re.I) else False
 1670 
 1671     OptionsInfo["DensityMapFiles"] = Options["--densityMapFiles"]
 1672     ProcessDensityMapFiles()
 1673 
 1674     OptionsInfo["Overwrite"] = Options["--overwrite"]
 1675     OptionsInfo["PMLOut"] = True if re.match("^Yes$", Options["--PMLOut"], re.I) else False
 1676 
 1677     OptionsInfo["Outfile"] = Options["--outfile"]
 1678     FileDir, FileName, FileExt = MiscUtil.ParseFileName(OptionsInfo["Outfile"])
 1679     OptionsInfo["PSEOut"] = False
 1680     if re.match("^pml$", FileExt, re.I):
 1681         OptionsInfo["PMLOutfile"] = OptionsInfo["Outfile"]
 1682         OptionsInfo["PMEOutfile"] = re.sub(".pml$", ".pme", OptionsInfo["Outfile"])
 1683     elif re.match("^pse$", FileExt, re.I):
 1684         OptionsInfo["PSEOut"] = True
 1685         OptionsInfo["PSEOutfile"] = OptionsInfo["Outfile"]
 1686         OptionsInfo["PMLOutfile"] = re.sub(".pse$", ".pml", OptionsInfo["Outfile"])
 1687         if os.path.exists(OptionsInfo["PMLOutfile"]) and (not OptionsInfo["Overwrite"]):
 1688             MiscUtil.PrintError(
 1689                 'The intermediate output file to be generated, %s, already exist. Use option "--ov" or "--overwrite" and try again.'
 1690                 % OptionsInfo["PMLOutfile"]
 1691             )
 1692 
 1693     OptionsInfo["LabelFontID"] = int(Options["--labelFontID"])
 1694 
 1695     # Process mesh parameters...
 1696     OptionsInfo["MeshCarveRadius"] = float(Options["--meshCarveRadius"])
 1697     OptionsInfo["MeshComplex"] = True if re.match("^Yes$", Options["--meshComplex"], re.I) else False
 1698     OptionsInfo["MeshChainComplex"] = Options["--meshChainComplex"]
 1699 
 1700     OptionsInfo["MeshWidth"] = float(Options["--meshWidth"])
 1701     OptionsInfo["MeshColor"] = Options["--meshColor"]
 1702 
 1703     OptionsInfo["MeshLevel"] = Options["--meshLevel"]
 1704     ProcessMeshLevel()
 1705 
 1706     OptionsInfo["SurfaceComplex"] = True if re.match("^Yes$", Options["--surfaceComplex"], re.I) else False
 1707     OptionsInfo["SurfaceChainComplex"] = Options["--surfaceChainComplex"]
 1708     OptionsInfo["SurfaceTransparency"] = float(Options["--surfaceTransparency"])
 1709 
 1710     OptionsInfo["PocketContactsLigandColor"] = Options["--pocketContactsLigandColor"]
 1711     OptionsInfo["PocketContactsLigandHydrophobicColor"] = Options["--pocketContactsLigandHydrophobicColor"]
 1712     OptionsInfo["PocketContactsSolventColor"] = Options["--pocketContactsSolventColor"]
 1713     OptionsInfo["PocketContactsInorganicColor"] = Options["--pocketContactsInorganicColor"]
 1714 
 1715     OptionsInfo["PocketContactsCutoff"] = float(Options["--pocketContactsCutoff"])
 1716     OptionsInfo["PocketDistanceCutoff"] = float(Options["--pocketDistanceCutoff"])
 1717 
 1718     OptionsInfo["PocketLabelColor"] = Options["--pocketLabelColor"]
 1719     OptionsInfo["PocketSurface"] = True if re.match("^Yes$", Options["--pocketSurface"], re.I) else False
 1720 
 1721     OptionsInfo["SelectionsChain"] = Options["--selectionsChain"]
 1722     OptionsInfo["SelectionsChainStyle"] = Options["--selectionsChainStyle"]
 1723     ProcessChainSelections()
 1724 
 1725     OptionsInfo["VolumeCarveRadius"] = float(Options["--volumeCarveRadius"])
 1726     OptionsInfo["VolumeComplex"] = True if re.match("^Yes$", Options["--volumeComplex"], re.I) else False
 1727     OptionsInfo["VolumeChainComplex"] = Options["--volumeChainComplex"]
 1728 
 1729     ProcessVolumeColorRamp()
 1730 
 1731     RetrieveInfilesInfo()
 1732     RetrieveRefFileInfo()
 1733 
 1734     OptionsInfo["ChainIDs"] = Options["--chainIDs"]
 1735     OptionsInfo["LigandIDs"] = Options["--ligandIDs"]
 1736 
 1737     ProcessChainAndLigandIDs()
 1738 
 1739 
 1740 def RetrieveOptions():
 1741     """Retrieve command line arguments and options."""
 1742 
 1743     # Get options...
 1744     global Options
 1745     Options = docopt(_docoptUsage_)
 1746 
 1747     # Set current working directory to the specified directory...
 1748     WorkingDir = Options["--workingdir"]
 1749     if WorkingDir:
 1750         os.chdir(WorkingDir)
 1751 
 1752     # Handle examples option...
 1753     if "--examples" in Options and Options["--examples"]:
 1754         MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_))
 1755         sys.exit(0)
 1756 
 1757 
 1758 def ValidateOptions():
 1759     """Validate option values."""
 1760 
 1761     MiscUtil.ValidateOptionTextValue("--align", Options["--align"], "yes no")
 1762     MiscUtil.ValidateOptionTextValue("--alignMethod", Options["--alignMethod"], "align cealign super")
 1763     MiscUtil.ValidateOptionTextValue("--alignMode", Options["--alignMode"], "FirstChain Complex")
 1764 
 1765     MiscUtil.ValidateOptionTextValue("--allowEmptyObjects", Options["--allowEmptyObjects"], "yes no")
 1766 
 1767     MiscUtil.ValidateOptionTextValue("--BFactorChainCartoonPutty", Options["--BFactorChainCartoonPutty"], "yes no")
 1768 
 1769     # Expand infiles to handle presence of multiple input files...
 1770     InfileNames = MiscUtil.ExpandFileNames(Options["--infiles"], ",")
 1771     if not len(InfileNames):
 1772         MiscUtil.PrintError('No input files specified for "-i, --infiles" option')
 1773 
 1774     # Validate file extensions...
 1775     for Infile in InfileNames:
 1776         MiscUtil.ValidateOptionFilePath("-i, --infiles", Infile)
 1777         MiscUtil.ValidateOptionFileExt("-i, --infiles", Infile, "pdb cif")
 1778         MiscUtil.ValidateOptionsDistinctFileNames("-i, --infiles", Infile, "-o, --outfile", Options["--outfile"])
 1779     Options["--infileNames"] = InfileNames
 1780 
 1781     MiscUtil.ValidateOptionFileExt("-o, --outfile", Options["--outfile"], "pml pse")
 1782     MiscUtil.ValidateOptionsOutputFileOverwrite(
 1783         "-o, --outfile", Options["--outfile"], "--overwrite", Options["--overwrite"]
 1784     )
 1785 
 1786     if re.match("^yes$", Options["--align"], re.I):
 1787         if not re.match("^FirstInputFile$", Options["--alignRefFile"], re.I):
 1788             AlignRefFile = Options["--alignRefFile"]
 1789             MiscUtil.ValidateOptionFilePath("--alignRefFile", AlignRefFile)
 1790             MiscUtil.ValidateOptionFileExt("--alignRefFile", AlignRefFile, "pdb cif")
 1791             MiscUtil.ValidateOptionsDistinctFileNames(
 1792                 "--AlignRefFile", AlignRefFile, "-o, --outfile", Options["--outfile"]
 1793             )
 1794 
 1795     MiscUtil.ValidateOptionTextValue("--ignoreHydrogens", Options["--ignoreHydrogens"], "yes no")
 1796 
 1797     MiscUtil.ValidateOptionTextValue("--PMLOut", Options["--PMLOut"], "yes no")
 1798     MiscUtil.ValidateOptionIntegerValue("--labelFontID", Options["--labelFontID"], {})
 1799 
 1800     MiscUtil.ValidateOptionFloatValue("--meshCarveRadius", Options["--meshCarveRadius"], {">": 0.0})
 1801     MiscUtil.ValidateOptionTextValue("--meshComplex", Options["--meshComplex"], "yes no")
 1802     MiscUtil.ValidateOptionTextValue("--meshChainComplex", Options["--meshChainComplex"], "yes no auto")
 1803     MiscUtil.ValidateOptionFloatValue("--meshWidth", Options["--meshWidth"], {">": 0.0})
 1804 
 1805     MiscUtil.ValidateOptionTextValue("--PMLOut", Options["--PMLOut"], "yes no")
 1806 
 1807     MiscUtil.ValidateOptionFloatValue("--pocketContactsCutoff", Options["--pocketContactsCutoff"], {">": 0.0})
 1808     MiscUtil.ValidateOptionFloatValue("--pocketDistanceCutoff", Options["--pocketDistanceCutoff"], {">": 0.0})
 1809     if float(Options["--pocketContactsCutoff"]) > float(Options["--pocketDistanceCutoff"]):
 1810         MiscUtil.PrintError(
 1811             'The value, %s, specified using option "--pocketContactsCutoff" must be less than value, %s, specified using "-pocketDistanceCutoff" option.'
 1812             % (Options["--pocketContactsCutoff"], Options["--pocketDistanceCutoff"])
 1813         )
 1814 
 1815     MiscUtil.ValidateOptionTextValue("--pocketSurface", Options["--pocketSurface"], "yes no")
 1816 
 1817     MiscUtil.ValidateOptionTextValue("--surfaceComplex", Options["--surfaceComplex"], "yes no")
 1818     MiscUtil.ValidateOptionTextValue("--surfaceChainComplex", Options["--surfaceChainComplex"], "yes no auto")
 1819     MiscUtil.ValidateOptionFloatValue("--surfaceTransparency", Options["--surfaceTransparency"], {">=": 0.0, "<=": 1.0})
 1820 
 1821     MiscUtil.ValidateOptionFloatValue("--volumeCarveRadius", Options["--volumeCarveRadius"], {">": 0.0})
 1822     MiscUtil.ValidateOptionTextValue("--volumeComplex", Options["--volumeComplex"], "yes no")
 1823     MiscUtil.ValidateOptionTextValue("--volumeChainComplex", Options["--volumeChainComplex"], "yes no auto")
 1824 
 1825 
 1826 # Setup a usage string for docopt...
 1827 _docoptUsage_ = """
 1828 PyMOLVisualizeCryoEMDensity.py - Visualize cryo-EM density
 1829 
 1830 Usage:
 1831     PyMOLVisualizeCryoEMDensity.py  [--align <yes or no>] [--alignMethod <align, cealign, super>]
 1832                                    [--alignMode <FirstChain or Complex>] [--alignRefFile <filename>]
 1833                                    [--allowEmptyObjects <yes or no>] [--BFactorChainCartoonPutty <yes or no>]
 1834                                    [--BFactorColorPalette <text> ] [--chainIDs <First, All or ID1,ID2...>]
 1835                                    [--densityMapFiles <file1,file2,file3,...>]
 1836                                    [--ignoreHydrogens <yes or no>] [--ligandIDs <Largest, All or ID1,ID2...>] [--labelFontID <number>]
 1837                                    [--meshCarveRadius <number>] [--meshComplex <yes or no>]
 1838                                    [--meshChainComplex <yes, no, or auto>] [--meshColor <text>]
 1839                                    [--meshLevel <number>] [--meshWidth <number>] [--PMLOut <yes or no>]
 1840                                    [--pocketContactsLigandColor <text>] [--pocketContactsLigandHydrophobicColor <text>]
 1841                                    [--pocketContactsSolventColor <text>]  [--pocketContactsCutoff <number>]
 1842                                    [--pocketContactsInorganicColor <text>] [--pocketDistanceCutoff <number>]
 1843                                    [--pocketLabelColor <text>] [--pocketSurface <yes or no>]
 1844                                    [--selectionsChain <ObjectName,SelectionSpec,...>] [--selectionsChainStyle <DisplayStyle>]
 1845                                    [--surfaceComplex <yes or no>] [--surfaceChainComplex <yes, no or auto>]
 1846                                    [--surfaceTransparency <number>] [--volumeCarveRadius <number>]
 1847                                    [--volumeComplex <yes or no>] [--volumeChainComplex <yes, no, or auto>]
 1848                                    [--volumeColorRamp <text>]   [--overwrite] [-w <dir>] -i <infile1,infile2,...> -o <outfile>
 1849     PyMOLVisualizeCryoEMDensity.py -h | --help | -e | --examples
 1850 
 1851 Description:
 1852     Generate PyMOL visualization files for viewing electron microscopy (EM) or
 1853     cryo-EM density around chains, ligands, and ligand binding pockets in
 1854     macromolecules including proteins and nucleic acids.
 1855 
 1856     The supported input file formats are: Macromolecule - PDB (.pdb) or CIF(.cif),
 1857     Cryo-EM Density - Collaborative Computational Project Number 4 (CCP4) ( .map)
 1858 
 1859     The supported output file formats are: PyMOL script file (.pml), PyMOL session
 1860     file (.pse)
 1861 
 1862     The cryo-EM density and header files along with PDB files may be downloaded
 1863     from appropriate servers using DownloadPDBFiles.pl script.
 1864 
 1865     A variety of PyMOL groups and objects may be  created for visualization of
 1866     cryo-EM density present in map files. These groups and objects correspond to
 1867     maps, volumes, meshes, surfaces,chains, ligands, inorganics, ligand binding
 1868     pockets, polar interactions, and pocket hydrophobic surfaces. A complete
 1869     hierarchy of all possible PyMOL groups and objects is shown below:
 1870     
 1871         <PDBFileRoot>
 1872             .Complex
 1873                 .Complex
 1874                 .CryoEM
 1875                     .Map
 1876                     .Volume
 1877                     .Mesh
 1878                     .Surface
 1879             .Chain<ID>
 1880                 .Complex
 1881                     .Complex
 1882                     .CryoEM
 1883                         .Volume
 1884                         .Mesh
 1885                         .Surface
 1886                 .Chain
 1887                     .Chain
 1888                     .BFactor
 1889                     .Selections
 1890                         .<Name1>
 1891                             .Selection
 1892                             .CryoEM
 1893                                 .Volume
 1894                                 .Mesh
 1895                                 .Surface
 1896                         .<Name2>
 1897                             ... ... ..
 1898                 .Solvent
 1899                 .Inorganic
 1900                 .Ligand<ID>
 1901                     .Ligand
 1902                         .Ligand
 1903                         .CryoEM
 1904                             .Volume
 1905                             .Mesh
 1906                             .Surface
 1907                     .Pocket
 1908                         .Pocket
 1909                         .CryoEM
 1910                             .Volume
 1911                             .Mesh
 1912                             .Surface
 1913                         .Polar_Contacts
 1914                         .Hydrophobic_Contacts
 1915                         .Surface
 1916                     .Pocket_Solvent
 1917                         .Pocket_Solvent
 1918                         .CryoEM
 1919                             .Volume
 1920                             .Mesh
 1921                             .Surface
 1922                         .Polar_Contacts
 1923                     .Pocket_Inorganic
 1924                         .Pocket_Inorganic
 1925                         .CryoEM
 1926                             .Volume
 1927                             .Mesh
 1928                             .Surface
 1929                         .Polar_Contacts
 1930                 .Ligand<ID>
 1931                     .Ligand
 1932                         ... ... ...
 1933                     .Pocket
 1934                         ... ... ...
 1935                     .Pocket_Solvent
 1936                         ... ... ...
 1937                     .Pocket_Inorganic
 1938                         ... ... ...
 1939             .Chain<ID>
 1940                 ... ... ...
 1941                 .Ligand<ID>
 1942                     ... ... ...
 1943                 .Ligand<ID>
 1944                     ... ... ...
 1945             .Chain<ID>
 1946                 ... ... ...
 1947         <PDBFileRoot>
 1948             .Complex
 1949                 ... ... ...
 1950             .Chain<ID>
 1951                 ... ... ...
 1952                 .Ligand<ID>
 1953                     ... ... ...
 1954                 .Ligand<ID>
 1955                     ... ... ...
 1956             .Chain<ID>
 1957                 ... ... ...
 1958     
 1959     The meshes, volumes, and surfaces  are not created for complete complex in input
 1960     files by default. A word to the wise: The creation of these mesh, volume, and surface
 1961     objects may slow down loading of PML file and generation of PSE file, based on the
 1962     size of input complex and map files. The generation of PSE file may also fail. In 
 1963     addition, you may want to interactively manipulate the contour level for meshes,
 1964     volumes, and surfaces. The recommended value for contour level is automatically
 1965     retrieved from header files available from EM density server. The recommended
 1966     value may not always work.
 1967 
 1968 Options:
 1969     -a, --align <yes or no>  [default: no]
 1970         Align input files to a reference file before visualization along with
 1971         available cryo-EM density map files.
 1972     --alignMethod <align, cealign, super>  [default: super]
 1973         Alignment methodology to use for aligning input files to a reference
 1974         file.
 1975     --alignMode <FirstChain or Complex>  [default: FirstChain]
 1976         Portion of input and reference files to use for spatial alignment of
 1977         input files against reference file.  Possible values: FirstChain or
 1978         Complex.
 1979         
 1980         The FirstChain mode allows alignment of the first chain in each input
 1981         file to the first chain in the reference file along with moving the rest
 1982         of the complex to coordinate space of the reference file. The complete
 1983         complex in each input file is aligned to the complete complex in reference
 1984         file for the Complex mode.
 1985     --alignRefFile <filename>  [default: FirstInputFile]
 1986         Reference input file name. The default is to use the first input file
 1987         name specified using '-i, --infiles' option.
 1988     --allowEmptyObjects <yes or no>  [default: no]
 1989         Allow creation of empty PyMOL objects corresponding to solvent and
 1990         inorganic atom selections across chains, ligands, and ligand binding pockets
 1991         in input file(s).
 1992     -b, --BFactorChainCartoonPutty <yes or no>  [default: yes]
 1993         A cartoon putty around individual chains colored by B factors. The minimum
 1994         and maximum values for B factors are automatically detected. These values
 1995         indicate spread of cryo-EM density around atoms. The 'blue_white_red' color
 1996         palette is deployed for coloring the cartoon putty.
 1997     --BFactorColorPalette <text>  [default: blue_white_red]
 1998         Color palette for coloring cartoon putty around chains generated using B
 1999         factors. Any valid PyMOL color palette name is allowed. No validation is
 2000         performed. The complete list of valid color palette names is a available
 2001         at: pymolwiki.org/index.php/Spectrum. Examples: blue_white_red,
 2002         blue_white_magenta, blue_red, green_white_red, green_red.
 2003     -c, --chainIDs <First, All or ID1,ID2...>  [default: First]
 2004         List of chain IDs to use for visualizing cryo-EM density. Possible values:
 2005         First, All, or a comma delimited list of chain IDs. The default is to use the
 2006         chain ID for the first chain in each input file.
 2007     -d, --densityMapFiles <file1,file2,file3,...>  [default: auto]
 2008         CryoEM density map file names. The EMDB ID is retrieved from PDB and CIF
 2009         file to set the cryo-EM density file name during automatic detection of
 2010         density files. The format of the file name is as follows:
 2011         
 2012             emd_<EMDBID>.map.gz or emd_<EMDBID>.map
 2013          
 2014         The density files must be present in the working directory.
 2015     -e, --examples
 2016         Print examples.
 2017     -h, --help
 2018         Print this help message.
 2019     -i, --infiles <infile1,infile2,infile3...>
 2020         Input file names.
 2021     --ignoreHydrogens <yes or no>  [default: yes]
 2022         Ignore hydrogens for ligand and pocket views.
 2023     -l, --ligandIDs <Largest, All or ID1,ID2...>  [default: Largest]
 2024         List of ligand IDs present in chains for visualizing cryo-EM density across
 2025         ligands and ligand binding pockets. Possible values: Largest, All, or a comma
 2026         delimited list of ligand IDs. The default is to use the largest ligand present
 2027         in all or specified chains in each input file.
 2028         
 2029         Ligands are identified using organic selection operator available in PyMOL.
 2030         It'll also  identify buffer molecules as ligands. The largest ligand contains
 2031         the highest number of heavy atoms.
 2032     --labelFontID <number>  [default: 7]
 2033         Font ID for drawing labels. Default: 7 (Sans Bold). Valid values: 5 to 16.
 2034         The specified value must be a valid PyMOL font ID. No validation is
 2035         performed. The complete lists of valid font IDs is available at:
 2036         pymolwiki.org/index.php/Label_font_id. Examples: 5 - Sans;
 2037         7 - Sans Bold; 9 - Serif; 10 - Serif Bold.
 2038     --meshCarveRadius <number>  [default: 1.6]
 2039         Radius in Angstroms around atoms for including cryo-EM density.
 2040     --meshComplex <yes or no>  [default: no]
 2041         Create meshes for complete complex in each input file using corresponding
 2042         density map file.
 2043     --meshChainComplex <yes, no, or auto>  [default: auto]
 2044         Create meshes for individual chain complex in each input file using
 2045         corresponding density map file. By default, the meshes are automatically
 2046         created for chain complexes without any ligands. 
 2047     --meshColor <text>  [default: blue]
 2048         Line color for meshes corresponding to density maps.. The specified value
 2049         must be valid color. No validation is performed.
 2050     --meshLevel <number1,number2,...>  [default: auto]
 2051         Comma delimited list of contour levels in sigma units for generating meshes
 2052         for each input file using corresponding density map file. The default is to
 2053         automatically retrieve the recommended contour levels for each input 
 2054         file. The header file emd-<EMDBID>.xml corresponding to an input file
 2055         must be present in the working directory  to automatically retrieve
 2056         recommended value for mesh contour level. Otherwise, the default contour
 2057         level is set to 1.
 2058         
 2059         You may want to interactively manipulate the contour level for meshes and
 2060         surfaces. The default recommended value may not always work.
 2061     --meshWidth <number>  [default: 0.5]
 2062         Line width for mesh lines corresponding to density maps.
 2063     -o, --outfile <outfile>
 2064         Output file name.
 2065     -p, --PMLOut <yes or no>  [default: yes]
 2066         Save PML file during generation of PSE file.
 2067     --pocketContactsLigandColor <text>  [default: orange]
 2068         Color for drawing polar contacts between ligand and pocket residues.
 2069         The specified value must be valid color. No validation is performed.
 2070     --pocketContactsLigandHydrophobicColor <text>  [default: purpleblue]
 2071         Color for drawing hydrophobic contacts between ligand and pocket residues.
 2072         The specified value must be valid color. No validation is performed. The
 2073         hydrophobic contacts are shown between pairs of carbon atoms not
 2074         connected to hydrogen bond donor or acceptors atoms as identified
 2075         by PyMOL.
 2076     --pocketContactsSolventColor <text>  [default: marine]
 2077         Color for drawing polar contacts between solvent and pocket residues.
 2078         The specified value must be valid color. No validation is performed.
 2079     --pocketContactsInorganicColor <text>  [default: deepsalmon]
 2080         Color for drawing polar contacts between inorganic and pocket residues.
 2081         The specified value must be valid color. No validation is performed.
 2082     --pocketContactsCutoff <number>  [default: 4.0]
 2083         Distance in Angstroms for identifying polar and hyrdophobic contacts
 2084         between atoms in pocket residues and ligands.
 2085     --pocketDistanceCutoff <number>  [default: 5.0]
 2086         Distance in Angstroms for identifying pocket residues around ligands.
 2087     --pocketLabelColor <text>  [default: magenta]
 2088         Color for drawing residue or atom level labels for a pocket. The specified
 2089         value must be valid color. No validation is performed.
 2090     --pocketSurface <yes or no>  [default: yes]
 2091         Hydrophobic surface around pocket. The pocket surface is colored by
 2092         hydrophobicity. It is only valid for proteins. The color of amino acids is
 2093         set using the Eisenberg hydrophobicity scale. The color varies from red
 2094         to white, red being the most hydrophobic amino acid.
 2095     --selectionsChain <ObjectName,SelectionSpec,...>  [default: None]
 2096         Custom selections for chains. It is a pairwise list of comma delimited values
 2097         corresponding to PyMOL object names and selection specifications.  The
 2098         selection specification must be a valid PyMOL specification. No validation is
 2099         performed.
 2100         
 2101         The PyMOL objects are created for each chain corresponding to the
 2102         specified selections. The display style for PyMOL objects is set using
 2103         value of '--selectionsChainStyle' option.
 2104         
 2105         The specified selection specification is automatically appended to appropriate
 2106         chain specification before creating PyMOL objects.
 2107         
 2108         For example, the following specification for '--selectionsChain' option will
 2109         generate PyMOL objects for chains containing Cysteines and Serines:
 2110             
 2111             Cysteines,resn CYS,Serines,resn SER
 2112             
 2113     --selectionsChainStyle <DisplayStyle>  [default: sticks]
 2114         Display style for PyMOL objects created for '--selectionsChain' option. It
 2115         must be a valid PyMOL display style. No validation is performed.
 2116     --surfaceComplex <yes or no>  [default: no]
 2117         Create surfaces for complete complex in input file(s) corresponding to density
 2118         map.
 2119     --surfaceChainComplex <yes, no or auto>  [default: auto]
 2120         Create surfaces for individual chain complexes in each input file using corresponding
 2121         density map file. By default, the surfaces are automatically created for chain complexes
 2122         without any ligands.
 2123     --surfaceTransparency <number>  [default: 0.25]
 2124         Surface transparency for molecular and cryo-EM density surfaces.
 2125     --overwrite
 2126         Overwrite existing files.
 2127     --volumeCarveRadius <number>  [default: 1.6]
 2128         Radius in Angstroms around atoms for including cryo-EM density.
 2129     --volumeComplex <yes or no>  [default: no]
 2130         Create volumes for complete complex in each input file using corresponding density
 2131         map file.
 2132     --volumeChainComplex <yes, no, or auto>  [default: auto]
 2133         Create volumes for individual chain complex in each input file using corresponding
 2134         density map file. By default, the volumes are automatically created for chain
 2135         complexes without any ligands.
 2136     --volumeColorRamp <text>  [default: auto]
 2137         Name of a volume color ramp for density map files. The specified value must
 2138         be a valid name. No validation is performed. The following volume color ramps
 2139         are currently available in PyMOL: default, 2fofc, fofc, rainbow, and rainbow2.
 2140         
 2141         The default is to automatically create a new volume color ramp for the first
 2142         input file using recommended contour level with an offset of 0.3 around this value.
 2143         The header file emd-<EMDBID>.xml must be present in the working directory  to
 2144         automatically retrieve recommended contour level and generate a  volume color ramp.
 2145         Otherwise, PyMOL default volume color ramp is employed to color volumes.
 2146         
 2147         The volume color ramp automatically created for the first input file is used for all
 2148         other input files.
 2149     -w, --workingdir <dir>
 2150         Location of working directory which defaults to the current directory.
 2151 
 2152 Examples:
 2153     To download structure and cryo-EM data for 5K12, 5UMD, 5W81, and 5UAK
 2154     before running the following examples, type:
 2155 
 2156         % DownloadPDBFiles.pl --DensityMap yes 5K12,5UMD,5W81,5UAK
 2157 
 2158     To visualize cryo-EM density at recommended contour level for the first
 2159     chain complex in a PDB file using corresponding density map and header
 2160     file, and generate a PML file type:
 2161 
 2162         % PyMOLVisualizeCryoEMDensity.py -i 5K12.pdb -o 5K12.pml
 2163 
 2164     To visualize cryo-EM density at recommended contour level for the first
 2165     chain complex in a PDB file and highlighting densities for all cysteines and
 2166     serines  using corresponding density map and header file, and generate
 2167     a PML file type:
 2168 
 2169         % PyMOLVisualizeCryoEMDensity.py -i 5K12.pdb -o 5K12.pml
 2170           --selectionsChain "Csysteines,resn cys,Serines,resn ser"
 2171 
 2172     To visualize electron density for the largest ligand in  chain K, and ligand
 2173     binding pocket to highlight ligand interactions with pockect residues,
 2174     solvents and inorganics, in a PDB and using corresponding map files, and
 2175     generate a PML file, type:
 2176 
 2177         % PyMOLVisualizeCryoEMDensity.py -c K -i 5UMD.cif -o 5UMD.pml
 2178 
 2179     To visualize cryo-EM density for all  chains along with any solvents in a
 2180     PDB file and using corresponding map files, and generate a PML file, type:
 2181 
 2182         % PyMOLVisualizeCryoEMDensity.py -c all -i 5K12.pdb -o 5K12.pml
 2183 
 2184     To visualize cryo-EM density at a specific contour level for the first chain
 2185     complex along with volume and surface in a PDB file using corresponding
 2186     to a specific density map file, and generate a PML file, type:
 2187 
 2188         % PyMOLVisualizeCryoEMDensity.py -d emd_8194.map.gz --meshLevel 1.0
 2189           --surfaceChainComplex yes --volumeChainComplex yes -i 5K12.pdb
 2190           -o 5K12.pml
 2191 
 2192     To align and visualize cryo-EM density at recommended contour levels for the
 2193     largest ligand in the first chain along with pockets or the first chain complex
 2194     in input files using corresponding maps and header files, type:
 2195 
 2196         % PyMOLVisualizeCryoEMDensity.py -a yes -i "5W81.pdb,5UAK.pdb"
 2197           -o SampleOut.pml
 2198 
 2199     To align and visualize cryo-EM density at recommended contour levels for all
 2200     chains and ligands in input files using specified density files, type:
 2201     in input files using corresponding maps and header files, type:
 2202 
 2203         % PyMOLVisualizeCryoEMDensity.py -a yes -i "5W81.pdb,5UAK.pdb"
 2204           -o SampleOut.pml -c all -l all -d "emd_8782.map.gz,emd_8516.map.gz"
 2205 
 2206 Author:
 2207     Manish Sud(msud@san.rr.com)
 2208 
 2209 See also:
 2210     DownloadPDBFiles.pl, PyMOLVisualizeCavities.py,
 2211     PyMOLVisualizeElectronDensity.py, PyMOLVisualizeInterfaces.py,
 2212     PyMOLVisualizeMacromolecules.py, PyMOLVisualizeSurfaceAndBuriedResidues.py
 2213 
 2214 Copyright:
 2215     Copyright (C) 2025 Manish Sud. All rights reserved.
 2216 
 2217     The functionality available in this script is implemented using PyMOL, a
 2218     molecular visualization system on an open source foundation originally
 2219     developed by Warren DeLano.
 2220 
 2221     This file is part of MayaChemTools.
 2222 
 2223     MayaChemTools is free software; you can redistribute it and/or modify it under
 2224     the terms of the GNU Lesser General Public License as published by the Free
 2225     Software Foundation; either version 3 of the License, or (at your option) any
 2226     later version.
 2227 
 2228 """
 2229 
 2230 if __name__ == "__main__":
 2231     main()