MayaChemTools

    1 #
    2 # File: PyMOLUtil.py
    3 # Author: Manish Sud <msud@san.rr.com>
    4 #
    5 # Copyright (C) 2026 Manish Sud. All rights reserved.
    6 #
    7 # The functionality available in this file is implemented using PyMOL, a
    8 # molecular visualization system on an open source foundation originally
    9 # developed by Warren DeLano.
   10 #
   11 # This file is part of MayaChemTools.
   12 #
   13 # MayaChemTools is free software; you can redistribute it and/or modify it under
   14 # the terms of the GNU Lesser General Public License as published by the Free
   15 # Software Foundation; either version 3 of the License, or (at your option) any
   16 # later version.
   17 #
   18 # MayaChemTools is distributed in the hope that it will be useful, but without
   19 # any warranty; without even the implied warranty of merchantability of fitness
   20 # for a particular purpose.  See the GNU Lesser General Public License for more
   21 # details.
   22 #
   23 # You should have received a copy of the GNU Lesser General Public License
   24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
   25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
   26 # Boston, MA, 02111-1307, USA.
   27 #
   28 
   29 from __future__ import print_function
   30 
   31 import os
   32 import re
   33 
   34 from pymol import cmd, stored, CmdException
   35 
   36 import MiscUtil
   37 
   38 __all__ = [
   39     "AreAminoAcidResiduesPresent",
   40     "AreNucleicAcidResiduesPresent",
   41     "CalculateCenterOfMass",
   42     "ConvertFileFormat",
   43     "ConvertPMLFileToPSEFile",
   44     "GetCentroid",
   45     "GetChains",
   46     "GetChainsAndLigandsInfo",
   47     "GetAminoAcidResiduesInfo",
   48     "GetInorganicResiduesInfo",
   49     "GetInterfaceChainsResiduesByCAlphaAtomsDistance",
   50     "GetInterfaceChainsResiduesByHeavyAtomsDistance",
   51     "GetInterfaceChainsResiduesBySASAChange",
   52     "GetLargestLigand",
   53     "GetLigandResiduesInfo",
   54     "GetLigands",
   55     "GetMolecules",
   56     "GetNucleicAcidResiduesInfo",
   57     "GetPocketInorganicResiduesInfo",
   58     "GetPocketPolymerResiduesInfo",
   59     "GetPocketSolventResiduesInfo",
   60     "GetPolymerResiduesInfo",
   61     "GetPhiPsiResiduesInfo",
   62     "GetPhiPsiChainsAndResiduesInfo",
   63     "GetPhiPsiCategoriesResiduesInfo",
   64     "GetSelectionResiduesInfo",
   65     "GetSolventResiduesInfo",
   66     "GetSurfaceAndBuriedResiduesInfo",
   67     "ProcessChainsAndLigandsOptionsInfo",
   68     "ProcessChainSelectionsOptionsInfo",
   69     "ProcessResidueTypesOptionsInfo",
   70     "ProcessSaltBridgesChainResiduesOptionsInfo",
   71     "ProcessSurfaceAtomTypesColorsOptionsInfo",
   72     "SetupPMLForAlignment",
   73     "SetupPMLForBFactorCartoonView",
   74     "SetupPMLForBFactorPuttyView",
   75     "SetupPMLForBallAndStickView",
   76     "SetupPMLForDeepColoring",
   77     "SetupPMLForDisulfideBondsView",
   78     "SetupPMLForEnableDisable",
   79     "SetupPMLForGroup",
   80     "SetupPMLForHydrophobicSurfaceView",
   81     "SetupPMLForHydrophobicAndChargeSurfaceView",
   82     "SetupPMLForInorganicView",
   83     "SetupPMLForLigandPocketInorganicView",
   84     "SetupPMLForLigandPocketSolventView",
   85     "SetupPMLForLigandPocketView",
   86     "SetupPMLForLigandView",
   87     "SetupPMLForLigandsInputFileView",
   88     "SetupPMLForDistanceContactsView",
   89     "SetupPMLForPiCationContactsView",
   90     "SetupPMLForPiPiContactsView",
   91     "SetupPMLForPolarContactsView",
   92     "SetupPMLForHalogenContactsView",
   93     "SetupPMLForHydrophobicContactsView",
   94     "SetupPMLForPolymerChainComplexView",
   95     "SetupPMLForPolymerChainView",
   96     "SetupPMLForPolymerComplexView",
   97     "SetupPMLForSolventView",
   98     "SetupPMLForSaltBridgesResiduesView",
   99     "SetupPMLForSurfaceView",
  100     "SetupPMLForSelectionDisplayView",
  101     "SetupPMLHeaderInfo",
  102 ]
  103 
  104 
  105 def GetMolecules(Selection="all"):
  106     """Get names of molecule objects in a selection or all molecule objects.
  107 
  108     Arguments:
  109         Selection (str): A PyMOL selection.
  110 
  111     Returns:
  112         list: Names of molecule objects.
  113 
  114     """
  115     Names = cmd.get_object_list("(" + Selection + ")")
  116 
  117     return Names
  118 
  119 
  120 def GetChains(MoleculeName, RemoveEmpty=True):
  121     """Get chain identifiers present in a molecule.
  122 
  123     Arguments:
  124         MoleculeName (str): Name of a PyMOL molecule object.
  125         RemoveEmpty (bool): Remove empty chain ID from the list of chain IDs
  126             returned by PyMOL.
  127 
  128     Returns:
  129         list: Names of chains present in a molecule, sorted alphabetically in a
  130             ascending order.
  131 
  132     """
  133     if not len(MoleculeName):
  134         return None
  135 
  136     ChainIDs = []
  137     try:
  138         ChainIDs = cmd.get_chains("model %s" % MoleculeName)
  139     except CmdException:
  140         MiscUtil.PrintWarning("PyMOLUtil.GetChains: Invalid molecule name: %s" % MoleculeName)
  141 
  142     if not len(ChainIDs):
  143         return None
  144 
  145     # Remove empty Chain IDs from the list...
  146     if RemoveEmpty:
  147         NonEmptyChainIDs = []
  148         for ChainID in ChainIDs:
  149             if len(ChainID):
  150                 NonEmptyChainIDs.append(ChainID)
  151         if len(NonEmptyChainIDs) != len(ChainIDs):
  152             MiscUtil.PrintInfo("PyMOLUtil.GetChains: Removing non-empty chain IDs from the list of chain IDs...")
  153 
  154         ChainIDs = NonEmptyChainIDs
  155 
  156     return ChainIDs
  157 
  158 
  159 def GetChainsAndLigandsInfo(
  160     Infile, MolName, Quite=False, LigandSortBy="Size", LigandSortOrder="Auto", LigandIgnoreHydrogens="Yes"
  161 ):
  162     """Get chain identifiers present in a molecule along with names of the
  163     ligands present in chains. Ligands are identified using PyMOL 'organic'
  164     selection.
  165 
  166     Arguments:
  167         Infile (str) : Name of a file.
  168         MolName (str) : Name to use for PyMOL molecule object.
  169         Quite (bool) : Flag
  170         LigandSortBy (str): Sort ligand names alphabetically or by size. Possible
  171             values: Alphabetical or Size
  172         LigandSortOrder (str): Sort order for sorting ligands. Possible values:
  173             Ascending, Descending, Auto. The 'Auto' value implies automatic
  174             determination of sort order based on the value of 'SortBy'.
  175             Automatic defaults: Descending for SortBy value of Size; Ascending
  176             for SortBy value of Alphabetical.
  177         LigandIgnoreHydrogens (str): Ignore hydrogens during determination of ligand
  178             size.
  179 
  180     Returns:
  181         dict: A dictionary containing list of chain identifiers and dictionaries
  182             of chains containing lists of ligand names for each chain. Names of
  183             ligands present in chain for a molecule sorted by size or
  184             alphabetically.
  185 
  186     Examples:
  187 
  188         ChainsAndLigandsInfo = GetChainsAndLigandsInfo(Infile, MolName)
  189         for ChainID in ChainsAndLigandsInfo["ChainIDs"]:
  190             for LigandID in ChainsAndLigandsInfo["LigandIDs"][ChainID]:
  191                 MiscUtil.PrintInfo("ChainID: %s; LigandID: %s" % (ChainID,
  192                     LigandID))
  193 
  194     """
  195     if not Quite:
  196         MiscUtil.PrintInfo("\nRetrieving chain and ligand information from input file %s..." % Infile)
  197 
  198     # Collect chains and ligands information with ligands sorted by size to be used for
  199     # identification of largest ligand at the top...
  200     cmd.load(Infile, MolName)
  201     ChainsAndLigandsInfo = _GetChainsAndLigands(MolName, LigandSortBy="size", LigandSortOrder="descending")
  202     cmd.delete(MolName)
  203 
  204     # Print out chain and ligand IDs...
  205     if not Quite:
  206         ChainIDs = ", ".join(ChainsAndLigandsInfo["ChainIDs"]) if len(ChainsAndLigandsInfo["ChainIDs"]) else "None"
  207         MiscUtil.PrintInfo("Chain IDs: %s" % ChainIDs)
  208 
  209         for ChainID in ChainsAndLigandsInfo["ChainIDs"]:
  210             LigandIDs = (
  211                 ", ".join(ChainsAndLigandsInfo["LigandIDs"][ChainID])
  212                 if len(ChainsAndLigandsInfo["LigandIDs"][ChainID])
  213                 else "None"
  214             )
  215             MiscUtil.PrintInfo("Chain ID: %s; LigandIDs: %s" % (ChainID, LigandIDs))
  216 
  217     return ChainsAndLigandsInfo
  218 
  219 
  220 def GetLigands(MoleculeName, ChainName, SortBy="Size", SortOrder="Auto", IgnoreHydrogens="Yes"):
  221     """Get names of ligands present in a chain of a  molecule. Ligands are
  222     identified using PyMOL 'organic' selection.
  223 
  224     Arguments:
  225         MoleculeName (str): Name of a PyMOL molecule object.
  226         ChainName (str): Name of a chain in a molecule.
  227         SortBy (str): Sort ligand names alphabetically or by size. Possible
  228             values: Alphabetical or Size
  229         SortOrder (str): Sort order for sorting ligands. Possible values:
  230             Ascending, Descending, Auto. The 'Auto' value implies automatic
  231             determination of sort order based on the value of 'SortBy'.
  232             Automatic defaults: Descending for SortBy value of Size; Ascending
  233             for SortBy value of Alphabetical.
  234         IgnoreHydrogens (str): Ignore hydrogens during determination of ligand
  235             size.
  236 
  237     Returns:
  238         list: Names of ligands present in chain for a molecule sorted by size
  239             or alphabetically.
  240 
  241     """
  242     if not (len(MoleculeName) and len(ChainName)):
  243         return None
  244 
  245     LigandsInfoMap = _GetLigandsInfo(MoleculeName, ChainName, SortBy, SortOrder, IgnoreHydrogens)
  246 
  247     LigandIDs = LigandsInfoMap["LigandResNames"]
  248     if not len(LigandIDs):
  249         LigandIDs = None
  250 
  251     return LigandIDs
  252 
  253 
  254 def GetLargestLigand(MoleculeName, ChainName, IgnoreHydrogens="Yes"):
  255     """Get name of the largest ligand for a chain present in a molecule. Ligands
  256     are identified using PyMOL 'organic' selection.
  257 
  258     Arguments:
  259         MoleculeName (str): Name of a PyMOL molecule object.
  260         ChainName (str): Name of a chain in a molecule.
  261         IgnoreHydrogens (str): Ignore hydrogens during determination of ligand
  262             size.
  263 
  264     Returns:
  265         str: Name of the largest ligand present in a chain.
  266 
  267     """
  268     if not (len(MoleculeName) and len(ChainName)):
  269         return None
  270 
  271     SortBy = "Size"
  272     SortOrder = "Descending"
  273     LigandsInfoMap = _GetLigandsInfo(MoleculeName, ChainName, SortBy, SortOrder, IgnoreHydrogens)
  274     LigandIDs = LigandsInfoMap["LigandResNames"]
  275 
  276     if len(LigandIDs):
  277         LigandID = LigandIDs[0]
  278     else:
  279         LigandID = None
  280 
  281     return LigandID
  282 
  283 
  284 def _GetChainsAndLigands(MoleculeName, LigandSortBy="Size", LigandSortOrder="Auto", LigandIgnoreHydrogens="Yes"):
  285     """Get chain identifiers in a molecule along with names of the ligands
  286     present in chains. Ligands are identified using PyMOL 'organic' selection.
  287     """
  288     if not len(MoleculeName):
  289         return None
  290 
  291     ChainIDs = GetChains(MoleculeName)
  292     if ChainIDs is None:
  293         return None
  294 
  295     ChainsAndLigandsMap = {}
  296     ChainsAndLigandsMap["ChainIDs"] = []
  297     ChainsAndLigandsMap["LigandIDs"] = {}
  298 
  299     for ChainID in ChainIDs:
  300         ChainsAndLigandsMap["ChainIDs"].append(ChainID)
  301         ChainsAndLigandsMap["LigandIDs"][ChainID] = []
  302 
  303         LigandIDs = GetLigands(
  304             MoleculeName, ChainID, SortBy=LigandSortBy, SortOrder=LigandSortOrder, IgnoreHydrogens=LigandIgnoreHydrogens
  305         )
  306         if LigandIDs is not None:
  307             ChainsAndLigandsMap["LigandIDs"][ChainID] = LigandIDs
  308 
  309     return ChainsAndLigandsMap
  310 
  311 
  312 def _GetLigandsInfo(MoleculeName, ChainName, SortBy="Size", SortOrder="Auto", IgnoreHydrogens="Yes"):
  313     """Retrieve information about ligands present in a chain of a molecule."""
  314 
  315     if not MiscUtil.CheckTextValue(SortBy, "Size Alphabetical"):
  316         MiscUtil.PrintError(
  317             "PyMOLUtil._GetLigandsInfo: The value specified, %s, for parameter SortBy is not valid. SupportedValues: Size Alphabetical"
  318             % SortBy
  319         )
  320 
  321     if not MiscUtil.CheckTextValue(SortOrder, "Ascending Descending Auto"):
  322         MiscUtil.PrintError(
  323             "PyMOLUtil._GetLigandsInfo: The value specified, %s, for parameter SortOrder is not valid. SupportedValues: Ascending Descending Auto"
  324             % SortOrder
  325         )
  326 
  327     if not MiscUtil.CheckTextValue(IgnoreHydrogens, "Yes No"):
  328         MiscUtil.PrintError(
  329             "PyMOLUtil._GetLigandsInfo: The value specified, %s, for parameter IgnoreHydrogens is not valid. SupportedValues: Yes No"
  330             % IgnoreHydrogens
  331         )
  332 
  333     SortBySize = True if re.match("^Size$", SortBy, re.I) else False
  334     if re.match("^Auto$", SortOrder, re.I):
  335         SortOrderDescending = True if re.match("^Size$", SortBy, re.I) else False
  336     else:
  337         SortOrderDescending = True if re.match("^Descending$", SortOrder, re.I) else False
  338     IgnoreHydrogenAtoms = True if re.match("^Yes$", IgnoreHydrogens, re.I) else False
  339 
  340     # Set up a command to retrieve all appropriate ligand atoms in organic ligands...
  341     SelectionCmd = "%s and chain %s and organic" % (MoleculeName, ChainName)
  342     if IgnoreHydrogenAtoms:
  343         SelectionCmd = "%s and not hydro" % (SelectionCmd)
  344 
  345     # Retrieve atoms...
  346     stored.LigandsInfo = []
  347     cmd.iterate(SelectionCmd, "stored.LigandsInfo.append([resi, resn])")
  348 
  349     # Retrieve ligands...
  350     LigandsInfoMap = {}
  351     LigandsInfoMap["LigandResNames"] = []
  352     LigandsInfoMap["LigandAtomCount"] = {}
  353     LigandsInfoMap["LigandResNumber"] = {}
  354 
  355     for LigandResNum, LigandResName in stored.LigandsInfo:
  356         if LigandResName in LigandsInfoMap["LigandResNames"]:
  357             LigandsInfoMap["LigandAtomCount"][LigandResName] += 1
  358         else:
  359             LigandsInfoMap["LigandResNames"].append(LigandResName)
  360             LigandsInfoMap["LigandAtomCount"][LigandResName] = 1
  361             LigandsInfoMap["LigandResNumber"][LigandResName] = LigandResNum
  362 
  363     if not len(LigandsInfoMap["LigandResNames"]):
  364         return LigandsInfoMap
  365 
  366     # Sort ligand names...
  367     ReverseOrder = True if SortOrderDescending else False
  368     if SortBySize:
  369         SortedLigandResNames = sorted(
  370             LigandsInfoMap["LigandResNames"],
  371             key=lambda LigandResName: LigandsInfoMap["LigandAtomCount"][LigandResName],
  372             reverse=ReverseOrder,
  373         )
  374     else:
  375         # Sort alphabetically...
  376         SortedLigandResNames = sorted(LigandsInfoMap["LigandResNames"], reverse=ReverseOrder)
  377 
  378     LigandsInfoMap["LigandResNames"] = SortedLigandResNames
  379 
  380     return LigandsInfoMap
  381 
  382 
  383 def GetCentroid(Selection):
  384     """Get centroid of a PyMOL selection.
  385 
  386     Arguments:
  387         MoleculeName (str): Name of a PyMOL selection.
  388 
  389     Returns:
  390         list or None: List of centroid values.
  391 
  392     """
  393 
  394     SelectionCmd = "(%s)" % Selection
  395 
  396     stored.CoordinatesInfo = []
  397     cmd.iterate_state(1, SelectionCmd, "stored.CoordinatesInfo.append([x, y, z])")
  398 
  399     XCoords = [Coords[0] for Coords in stored.CoordinatesInfo]
  400     YCoords = [Coords[1] for Coords in stored.CoordinatesInfo]
  401     ZCoords = [Coords[2] for Coords in stored.CoordinatesInfo]
  402 
  403     NumOfCoords = len(stored.CoordinatesInfo)
  404 
  405     CentroidX = sum(XCoords) / NumOfCoords
  406     CentroidY = sum(YCoords) / NumOfCoords
  407     CentroidZ = sum(ZCoords) / NumOfCoords
  408 
  409     return [CentroidX, CentroidY, CentroidZ]
  410 
  411 
  412 def AreAminoAcidResiduesPresent(MoleculeName, ChainName, Type="Any"):
  413     """Check for the presence of amino acid residues in a chain of a
  414     molecule. Chains are identified using PyMOL 'polymer' selection.
  415     Nonstandard amino acid residues correspond to all residues other than
  416     the standard amino acids and nucleic acids. Any amino acid residues cover
  417     all residues other than the standard nucleic acids.
  418 
  419     Arguments:
  420         MoleculeName (str): Name of a PyMOL molecule object.
  421         ChainName (str): Name of a chain in a molecule.
  422         Type (str): Types of amino acids: Standard, NonStandard, Any
  423 
  424     Returns:
  425         boolean: True or False.
  426 
  427     """
  428 
  429     if not (len(MoleculeName) and len(ChainName)):
  430         return False
  431 
  432     if not re.match("^(Standard|NonStandard|Any)$", Type, re.I):
  433         MiscUtil.PrintError("PyMOLUtil.AreAminoAcidResiduesPresent: Invalid amino acid type: %s" % Type)
  434 
  435     SelectionCmd = _SetupAminoAcidResiduesSelectionCmd(MoleculeName, ChainName, Type)
  436 
  437     Status = True if cmd.count_atoms(SelectionCmd) else False
  438 
  439     return Status
  440 
  441 
  442 def AreNucleicAcidResiduesPresent(MoleculeName, ChainName):
  443     """Check for the presence of nucleic acid residues in a chain of a
  444     molecule. Chains are identified using PyMOL 'polymer' selection.
  445 
  446     Arguments:
  447         MoleculeName (str): Name of a PyMOL molecule object.
  448         ChainName (str): Name of a chain in a molecule.
  449 
  450     Returns:
  451         boolean: True or False.
  452 
  453     """
  454 
  455     if not (len(MoleculeName) and len(ChainName)):
  456         return False
  457 
  458     ResidueNames = _GetNucleicAcidResidueNames()
  459     if not len(ResidueNames):
  460         return False
  461 
  462     ResidueNamesSelection = "+".join(ResidueNames)
  463     SelectionCmd = "(%s and chain %s and polymer and (resn %s))" % (MoleculeName, ChainName, ResidueNamesSelection)
  464 
  465     Status = True if cmd.count_atoms(SelectionCmd) else False
  466 
  467     return Status
  468 
  469 
  470 def _SetupAminoAcidResiduesSelectionCmd(MoleculeName, ChainName, Type):
  471     """Set up amino acids selection command for PyMOL."""
  472 
  473     AminoAcidsSelection = "+".join(_GetAminoAcidResidueNames())
  474     NucleicAcidsSelection = "+".join(_GetNucleicAcidResidueNames())
  475 
  476     if re.match("^Standard$", Type, re.I):
  477         SelectionCmd = "(%s and chain %s and polymer and (resn %s))" % (MoleculeName, ChainName, AminoAcidsSelection)
  478     elif re.match("^NonStandard$", Type, re.I):
  479         SelectionCmd = "(%s and chain %s and polymer and (not ((resn %s) or (resn %s))))" % (
  480             MoleculeName,
  481             ChainName,
  482             AminoAcidsSelection,
  483             NucleicAcidsSelection,
  484         )
  485     else:
  486         # Any amino acid resiudes...
  487         SelectionCmd = "(%s and chain %s and polymer and (not (resn %s)))" % (
  488             MoleculeName,
  489             ChainName,
  490             NucleicAcidsSelection,
  491         )
  492 
  493     return SelectionCmd
  494 
  495 
  496 def _GetAminoAcidResidueNames():
  497     """Get list of amino acid residue names."""
  498 
  499     ResidueNames = [
  500         "ALA",
  501         "ARG",
  502         "ASN",
  503         "ASP",
  504         "CYS",
  505         "GLN",
  506         "GLU",
  507         "GLY",
  508         "HIS",
  509         "ILE",
  510         "LEU",
  511         "LYS",
  512         "MET",
  513         "PHE",
  514         "PRO",
  515         "SER",
  516         "THR",
  517         "TRP",
  518         "TYR",
  519         "VAL",
  520     ]
  521 
  522     return ResidueNames
  523 
  524 
  525 def _GetNucleicAcidResidueNames():
  526     """Get list of nucleic acid residue names."""
  527 
  528     ResidueNames = ["A", "G", "T", "U", "C", "DA", "DG", "DT", "DU", "DC"]
  529 
  530     return ResidueNames
  531 
  532 
  533 def GetPolymerResiduesInfo(MoleculeName, ChainName):
  534     """Get information for residues present in a chain of a  molecule.
  535     Chains are identified using PyMOL 'polymer' selection.
  536 
  537     Arguments:
  538         MoleculeName (str): Name of a PyMOL molecule object.
  539         ChainName (str): Name of a chain in a molecule.
  540 
  541     Returns:
  542         dict: A dictionary containing list of residue names and dictionaries of
  543             residue numbers and residue count for each residue. Names of
  544             residues in the dictionary are not sorted.
  545 
  546     Examples:
  547 
  548         ResiduesInfo = GetPolymerResiduesInfo(MolName, ChainName)
  549         for ResName in ResiduesInfo["ResNames"]:
  550             ResCount = ResiduesInfo["ResCount"][ResName]
  551             ResNums = ResiduesInfo["ResNum"][ResName]
  552             MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums: %s" %
  553                 (ResName, ResCount, ResNums))
  554 
  555     """
  556     if not (len(MoleculeName) and len(ChainName)):
  557         return None
  558 
  559     SelectionCmd = "%s and chain %s and polymer" % (MoleculeName, ChainName)
  560     ResiduesInfoMap = _GetSelectionResiduesInfo(SelectionCmd)
  561 
  562     return ResiduesInfoMap
  563 
  564 
  565 def GetAminoAcidResiduesInfo(MoleculeName, ChainName, Type="Standard"):
  566     """Get information for amino acid residues present in a chain of a
  567     molecule. Chains are identified using PyMOL 'polymer' selection.
  568     Nonstandard amino acid residues correspond to all residues other than
  569     the standard amino acids and nucleic acids. Any amino acid residues cover
  570     all residues other than the standard nucleic acids.
  571 
  572     Arguments:
  573         MoleculeName (str): Name of a PyMOL molecule object.
  574         ChainName (str): Name of a chain in a molecule.
  575         Type (str): Types of amino acids: Standard, NonStandard, Any
  576 
  577     Returns:
  578         dict: A dictionary containing list of residue names and dictionaries of
  579             residue numbers and residue count for each residue. Names of
  580             residues in the dictionary are not sorted.
  581 
  582     Examples:
  583 
  584         ResiduesInfo = GetPolymerResiduesInfo(MolName, ChainName)
  585         for ResName in ResiduesInfo["ResNames"]:
  586             ResCount = ResiduesInfo["ResCount"][ResName]
  587             ResNums = ResiduesInfo["ResNum"][ResName]
  588             MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums: %s" %
  589                 (ResName, ResCount, ResNums))
  590 
  591     """
  592 
  593     if not (len(MoleculeName) and len(ChainName)):
  594         return None
  595 
  596     if not re.match("^(Standard|NonStandard|Any)$", Type, re.I):
  597         MiscUtil.PrintError("PyMOLUtil.GetAminoAcidResiduesInfo: Invalid amino acid type: %s" % Type)
  598 
  599     SelectionCmd = _SetupAminoAcidResiduesSelectionCmd(MoleculeName, ChainName, Type)
  600     ResiduesInfoMap = _GetSelectionResiduesInfo(SelectionCmd)
  601 
  602     return ResiduesInfoMap
  603 
  604 
  605 def GetNucleicAcidResiduesInfo(MoleculeName, ChainName):
  606     """Get information for nucleic acid residues present in a chain of
  607     a  molecule. Chains are identified using PyMOL 'polymer' selection.
  608 
  609     Arguments:
  610         MoleculeName (str): Name of a PyMOL molecule object.
  611         ChainName (str): Name of a chain in a molecule.
  612 
  613     Returns:
  614         dict: A dictionary containing list of residue names and dictionaries of
  615             residue numbers and residue count for each residue. Names of
  616             residues in the dictionary are not sorted.
  617 
  618     Examples:
  619 
  620         ResiduesInfo = GetPolymerResiduesInfo(MolName, ChainName)
  621         for ResName in ResiduesInfo["ResNames"]:
  622             ResCount = ResiduesInfo["ResCount"][ResName]
  623             ResNums = ResiduesInfo["ResNum"][ResName]
  624             MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums: %s" %
  625                 (ResName, ResCount, ResNums))
  626 
  627     """
  628 
  629     if not (len(MoleculeName) and len(ChainName)):
  630         return None
  631 
  632     ResidueNames = _GetNucleicAcidResidueNames()
  633 
  634     ResidueNamesSelection = "+".join(ResidueNames)
  635     SelectionCmd = "(%s and chain %s and polymer and (resn %s))" % (MoleculeName, ChainName, ResidueNamesSelection)
  636 
  637     ResiduesInfoMap = _GetSelectionResiduesInfo(SelectionCmd)
  638 
  639     return ResiduesInfoMap
  640 
  641 
  642 def GetSelectionResiduesInfo(SelectionCmd):
  643     """Get information for residues in a chain specified by a selection command.
  644 
  645     Arguments:
  646         SelectionCmd (str): PyMOL selection command.
  647 
  648     Returns:
  649         dict: A dictionary containing list of residue names and dictionaries of
  650             residue numbers and residue count for each residue. Names of
  651             residues in the dictionary are not sorted.
  652 
  653     Examples:
  654 
  655         ResiduesInfo = GetSelectionResiduesInfo(SelectionCmd)
  656         for ResName in ResiduesInfo["ResNames"]:
  657             ResCount = ResiduesInfo["ResCount"][ResName]
  658             ResNums = ResiduesInfo["ResNum"][ResName]
  659             MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums: %s" %
  660                 (ResName, ResCount, ResNums))
  661 
  662     """
  663     if not len(SelectionCmd):
  664         return None
  665 
  666     ResiduesInfoMap = _GetSelectionResiduesInfo(SelectionCmd)
  667 
  668     return ResiduesInfoMap
  669 
  670 
  671 def GetSolventResiduesInfo(MoleculeName, ChainName):
  672     """Get information for solvent residues present in a chain of a  molecule.
  673     Solvents are identified using PyMOL 'solvent' selection.
  674 
  675     Arguments:
  676         MoleculeName (str): Name of a PyMOL molecule object.
  677         ChainName (str): Name of a chain in a molecule.
  678 
  679     Returns:
  680         dict: A dictionary containing list of residue names and dictionaries of
  681             residue numbers and residue count for each residue. Names of
  682             residues in the dictionary are not sorted.
  683 
  684     Examples:
  685 
  686         ResiduesInfo = GetSolventResiduesInfo(MolName, ChainName)
  687         for ResName in ResiduesInfo["ResNames"]:
  688             ResCount = ResiduesInfo["ResCount"][ResName]
  689             ResNums = ResiduesInfo["ResNum"][ResName]
  690             MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums: %s" %
  691                 (ResName, ResCount, ResNums))
  692 
  693     """
  694     if not (len(MoleculeName) and len(ChainName)):
  695         return None
  696 
  697     SelectionCmd = "%s and chain %s and solvent" % (MoleculeName, ChainName)
  698     ResiduesInfoMap = _GetSelectionResiduesInfo(SelectionCmd)
  699 
  700     return ResiduesInfoMap
  701 
  702 
  703 def GetInorganicResiduesInfo(MoleculeName, ChainName):
  704     """Get information for inorganic residues present in a chain of a  molecule.
  705     Inorganic residues are identified using PyMOL 'inorganic' selection.
  706 
  707     Arguments:
  708         MoleculeName (str): Name of a PyMOL molecule object.
  709         ChainName (str): Name of a chain in a molecule.
  710 
  711     Returns:
  712         dict: A dictionary containing list of residue names and dictionaries of
  713             residue numbers and residue count for each residue. Names of
  714             residues in the dictionary are not sorted.
  715 
  716     Examples:
  717 
  718         ResiduesInfo = GetInorganicResiduesInfo(MolName, ChainName)
  719         for ResName in ResiduesInfo["ResNames"]:
  720             ResCount = ResiduesInfo["ResCount"][ResName]
  721             ResNums = ResiduesInfo["ResNum"][ResName]
  722             MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums: %s" %
  723                 (ResName, ResCount, ResNums))
  724 
  725     """
  726     if not (len(MoleculeName) and len(ChainName)):
  727         return None
  728 
  729     SelectionCmd = "%s and chain %s and inorganic" % (MoleculeName, ChainName)
  730     ResiduesInfoMap = _GetSelectionResiduesInfo(SelectionCmd)
  731 
  732     return ResiduesInfoMap
  733 
  734 
  735 def GetLigandResiduesInfo(MoleculeName, ChainName):
  736     """Get information for ligand residues present in a chain of a  molecule.
  737     Ligands are identified using PyMOL 'organic' selection.
  738 
  739     Arguments:
  740         MoleculeName (str): Name of a PyMOL molecule object.
  741         ChainName (str): Name of a chain in a molecule.
  742 
  743     Returns:
  744         dict: A dictionary containing list of residue names and dictionaries of
  745             residue numbers and residue count for each residue. Names of
  746             residues in the dictionary are not sorted.
  747 
  748     Examples:
  749 
  750         ResiduesInfo = GetLigandResiduesInfo(MolName, ChainName)
  751         for ResName in ResiduesInfo["ResNames"]:
  752             ResCount = ResiduesInfo["ResCount"][ResName]
  753             ResNums = ResiduesInfo["ResNum"][ResName]
  754             MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums: %s" %
  755                 (ResName, ResCount, ResNums))
  756 
  757     """
  758     if not (len(MoleculeName) and len(ChainName)):
  759         return None
  760 
  761     SelectionCmd = "%s and chain %s and organic" % (MoleculeName, ChainName)
  762     ResiduesInfoMap = _GetSelectionResiduesInfo(SelectionCmd)
  763 
  764     return ResiduesInfoMap
  765 
  766 
  767 def GetPocketPolymerResiduesInfo(MoleculeName, ChainName, LigandResName, LigandResNum, PocketDistanceCutoff):
  768     """Get information for chain residues present in a pocket around a ligand
  769     in a molecule. Polymer residues are identified using negation of PyMOL
  770     selection operators 'organic', 'solvent', and 'inorganic'.
  771 
  772     Arguments:
  773         MoleculeName (str): Name of a PyMOL molecule object.
  774         ChainName (str): Name of a chain in a molecule.
  775         LigandResName (str): Residue name of a ligand in a chain.
  776         LigandResNum (str): Residue number of a ligand in a chain.
  777         PocketDistanceCutoff (float): Distance around ligand to identify pocket
  778             residues.
  779 
  780     Returns:
  781         dict: A dictionary containing list of residue names and dictionaries of
  782             residue numbers and residue count for each residue. Names of
  783             residues in the dictionary are not sorted.
  784 
  785     Examples:
  786 
  787         ResiduesInfo = GetPocketPolymerResiduesInfo(MolName, ChainName)
  788         for ResName in ResiduesInfo["ResNames"]:
  789             ResCount = ResiduesInfo["ResCount"][ResName]
  790             ResNums = ResiduesInfo["ResNum"][ResName]
  791             MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums: %s" %
  792                 (ResName, ResCount, ResNums))
  793 
  794     """
  795     if not (len(MoleculeName) and len(ChainName) and len(LigandResName) and len(LigandResNum)):
  796         return None
  797 
  798     LigandSelection = "%s and chain %s and organic and resn %s and resi %s" % (
  799         MoleculeName,
  800         ChainName,
  801         LigandResName,
  802         LigandResNum,
  803     )
  804     MoleculeSelection = "%s and chain %s" % (MoleculeName, ChainName)
  805     SelectionCmd = "((byresidue (%s) within %.1f of (%s))  and (not solvent) and (not inorganic) and (not organic))" % (
  806         MoleculeSelection,
  807         PocketDistanceCutoff,
  808         LigandSelection,
  809     )
  810 
  811     ResiduesInfoMap = _GetSelectionResiduesInfo(SelectionCmd)
  812 
  813     return ResiduesInfoMap
  814 
  815 
  816 def GetPocketSolventResiduesInfo(MoleculeName, ChainName, LigandResName, LigandResNum, PocketDistanceCutoff):
  817     """Get information for solvent residues present in a pocket around a ligand
  818     in a molecule. Solvent residues are identified using PyMOL 'solvent'
  819     selection.
  820 
  821     Arguments:
  822         MoleculeName (str): Name of a PyMOL molecule object.
  823         ChainName (str): Name of a chain in a molecule.
  824         LigandResName (str): Residue name of a ligand in a chain.
  825         LigandResNum (str): Residue number of a ligand in a chain.
  826         PocketDistanceCutoff (float): Distance around ligand to identify pocket
  827             residues.
  828 
  829     Returns:
  830         dict: A dictionary containing list of residue names and dictionaries of
  831             residue numbers and residue count for each residue. Names of
  832             residues in the dictionary are not sorted.
  833 
  834     Examples:
  835 
  836         ResiduesInfo = GetPocketSolventResiduesInfo(MolName, ChainName)
  837         for ResName in ResiduesInfo["ResNames"]:
  838             ResCount = ResiduesInfo["ResCount"][ResName]
  839             ResNums = ResiduesInfo["ResNum"][ResName]
  840             MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums: %s" %
  841                 (ResName, ResCount, ResNums))
  842 
  843     """
  844     if not (len(MoleculeName) and len(ChainName) and len(LigandResName) and len(LigandResNum)):
  845         return None
  846 
  847     LigandSelection = "%s and chain %s and organic and resn %s and resi %s" % (
  848         MoleculeName,
  849         ChainName,
  850         LigandResName,
  851         LigandResNum,
  852     )
  853     MoleculeSelection = "%s and chain %s" % (MoleculeName, ChainName)
  854     SelectionCmd = "((byresidue (%s) within %.1f of (%s))  and solvent)" % (
  855         MoleculeSelection,
  856         PocketDistanceCutoff,
  857         LigandSelection,
  858     )
  859 
  860     ResiduesInfoMap = _GetSelectionResiduesInfo(SelectionCmd)
  861 
  862     return ResiduesInfoMap
  863 
  864 
  865 def GetPocketInorganicResiduesInfo(MoleculeName, ChainName, LigandResName, LigandResNum, PocketDistanceCutoff):
  866     """Get information for inorganic residues present in a pocket around a
  867     ligand in a molecule. Inorganic residues are identified using PyMOL
  868     'inorganic' selection.
  869 
  870     Arguments:
  871         MoleculeName (str): Name of a PyMOL molecule object.
  872         ChainName (str): Name of a chain in a molecule.
  873         LigandResName (str): Residue name of a ligand in a chain.
  874         LigandResNum (str): Residue number of a ligand in a chain.
  875         PocketDistanceCutoff (float): Distance around a ligand to identify
  876             pocket residues.
  877 
  878     Returns:
  879         dict: A dictionary containing list of residue names and dictionaries of
  880             residue numbers and residue count for each residue. Names of
  881             residues in the dictionary are not sorted.
  882 
  883     Examples:
  884 
  885         ResiduesInfo = GetPocketInorganicResiduesInfo(MolName, ChainName)
  886         for ResName in ResiduesInfo["ResNames"]:
  887             ResCount = ResiduesInfo["ResCount"][ResName]
  888             ResNums = ResiduesInfo["ResNum"][ResName]
  889             MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums: %s" %
  890                 (ResName, ResCount, ResNums))
  891 
  892     """
  893     if not (len(MoleculeName) and len(ChainName) and len(LigandResName) and len(LigandResNum)):
  894         return None
  895 
  896     LigandSelection = "%s and chain %s and organic and resn %s and resi %s" % (
  897         MoleculeName,
  898         ChainName,
  899         LigandResName,
  900         LigandResNum,
  901     )
  902     MoleculeSelection = "%s and chain %s" % (MoleculeName, ChainName)
  903     SelectionCmd = "((byresidue (%s) within %.1f of (%s))  and inorganic)" % (
  904         MoleculeSelection,
  905         PocketDistanceCutoff,
  906         LigandSelection,
  907     )
  908 
  909     ResiduesInfoMap = _GetSelectionResiduesInfo(SelectionCmd)
  910 
  911     return ResiduesInfoMap
  912 
  913 
  914 def GetPhiPsiResiduesInfo(MoleculeName, ChainName, Categorize=True):
  915     """Get phi and psi torsion angle information for residues in a chain of a
  916     molecule containing amino acids.
  917 
  918     The phi and psi angles are optionally categorized into the following groups
  919     corresponding to four types of  Ramachandran plots:
  920 
  921     General: All residues except glycine, proline, or pre-proline
  922     Glycine: Only glycine residues
  923     Proline: Only proline residues
  924     Pre-Proline: Only residues before proline not including glycine or proline
  925 
  926     Arguments:
  927         MoleculeName (str): Name of a PyMOL molecule object.
  928         ChainName (str): Name of a chain in a molecule.
  929 
  930     Returns:
  931         dict: A dictionary containing sorted list of residue numbers and
  932             dictionaries of residue names, phi and psi angles for each residue
  933             number.
  934 
  935     Examples:
  936 
  937         PhiPsiInfoMap = GetPhiPsiResiduesInfo(MolName, ChainName, True)
  938         for ResNum in PhiPsiInfoMap["ResNums"]:
  939             ResName = PhiPsiInfoMap["ResName"][ResNum]
  940             Phi = PhiPsiInfoMap["Phi"][ResNum]
  941             Psi = PhiPsiInfoMap["Psi"][ResNum]
  942             Category = PhiPsiInfoMap["Category"][ResNum]
  943             MiscUtil.PrintInfo("ResNum: %s; ResName: %s; Phi: %8.2f;
  944                 Psi: %8.2f; Category: %s" % (ResNum, ResName, Phi, Psi,
  945                 Categorize))
  946 
  947     """
  948     if not (len(MoleculeName) and len(ChainName)):
  949         return None
  950 
  951     SelectionCmd = "%s and chain %s" % (MoleculeName, ChainName)
  952     PhiPsiResiduesInfoMap = _GetSelectionPhiPsiResiduesInfo(SelectionCmd, Categorize)
  953 
  954     return PhiPsiResiduesInfoMap
  955 
  956 
  957 def GetPhiPsiChainsAndResiduesInfo(MoleculeName, Categorize=True):
  958     """Get phi and psi torsion angle information for residues across chains in
  959     a molecule containing amino acids.
  960 
  961     The phi and psi angles are optionally categorized into the following groups
  962     corresponding to four types of  Ramachandran plots:
  963 
  964     General: All residues except glycine, proline, or pre-proline
  965     Glycine: Only glycine residues
  966     Proline: Only proline residues
  967     Pre-Proline: Only residues before proline not including glycine or proline
  968 
  969     Arguments:
  970         MoleculeName (str): Name of a PyMOL molecule object.
  971 
  972     Returns:
  973         dict: A dictionary containing sorted list of residue numbers for each
  974             chain and dictionaries of residue names, phi and psi angles for each
  975             residue number.
  976 
  977     Examples:
  978 
  979         PhiPsiInfoMap = GetPhiPsiChainsAndResiduesInfo(MolName)
  980         for ChainID in PhiPsiInfoMap["ChainIDs"]:
  981             for ResNum in PhiPsiInfoMap["ResNums"][ChainID]:
  982                 ResName = PhiPsiInfoMap["ResName"][ChainID][ResNum]
  983                 Phi = PhiPsiInfoMap["Phi"][ChainID][ResNum]
  984                 Psi = PhiPsiInfoMap["Psi"][ChainID][ResNum]
  985                 Category = PhiPsiInfoMap["Category"][ChainID][ResNum]
  986                 MiscUtil.PrintInfo("ChainID: %s; ResNum: %s; ResName: %s; Phi: %8.2f;
  987                     Psi: %8.2f; Category: %s" % (ChainID, ResNum, ResName, Phi,
  988                     Psi, Category))
  989 
  990     """
  991     if not len(MoleculeName):
  992         return None
  993 
  994     SelectionCmd = "%s" % (MoleculeName)
  995     PhiPsiResiduesInfoMap = _GetSelectionPhiPsiChainsAndResiduesInfo(SelectionCmd, Categorize)
  996 
  997     return PhiPsiResiduesInfoMap
  998 
  999 
 1000 def GetPhiPsiCategoriesResiduesInfo(MoleculeName, ChainName):
 1001     """Get phi and psi torsion angle information for residues in a chain of a
 1002     molecule containing amino acids.
 1003 
 1004     The phi and psi angles are optionally categorized into the following groups
 1005     corresponding to four types of  Ramachandran plots:
 1006 
 1007     General: All residues except glycine, proline, or pre-proline
 1008     Glycine: Only glycine residues
 1009     Proline: Only proline residues
 1010     Pre-Proline: Only residues before proline not including glycine or proline
 1011 
 1012     Arguments:
 1013         MoleculeName (str): Name of a PyMOL molecule object.
 1014         ChainName (str): Name of a chain in a molecule.
 1015 
 1016     Returns:
 1017         dict1: Phi and psi angle information for residues in General category.
 1018             It's a dictionary containing sorted list of residue numbers and
 1019             dictionaries of residue names, phi and psi angles for each residue
 1020             number.
 1021         dict2: Phi and psi angle information for residues in Gly category.
 1022         dict3: Phi and psi angle information for residues in Pro category.
 1023         dict2: Phi and psi angle information for residues in Pre-Pro category.
 1024 
 1025     Examples:
 1026 
 1027         GeneralPhiPsiInfo, GlyPhiPsiInfo, ProPhiPsiInfo, PreProPhiPsiInfo =
 1028             GetPhiPsiCategoriesResiduesInfo(MolName, ChainID)
 1029         for ResNum in GeneralPhiPsiInfo["ResNums"]:
 1030             ResName = GeneralPhiPsiInfo["ResName"][ResNum]
 1031             Phi = GeneralPhiPsiInfo["Phi"][ResNum]
 1032             Psi = GeneralPhiPsiInfo["Psi"][ResNum]
 1033             MiscUtil.PrintInfo("ResNum: %s; ResName: %s;
 1034                 Phi: %8.2f; Psi: %8.2f" % (ResNum, ResName, Phi, Psi))
 1035 
 1036     """
 1037     if not (len(MoleculeName) and len(ChainName)):
 1038         return None
 1039 
 1040     SelectionCmd = "%s and chain %s" % (MoleculeName, ChainName)
 1041     GeneralPhiPsiInfo, GlyPhiPsiInfo, ProPhiPsiInfo, PreProPhiPsiInfo = _GetSelectionPhiPsiCategoriesResiduesInfo(
 1042         SelectionCmd
 1043     )
 1044 
 1045     return GeneralPhiPsiInfo, GlyPhiPsiInfo, ProPhiPsiInfo, PreProPhiPsiInfo
 1046 
 1047 
 1048 def GetSurfaceAndBuriedResiduesInfo(MoleculeName, ChainName, SASACutoff=2.5):
 1049     """Get information for surafce and buried residues present in a chain of a
 1050     molecule. The surface residues correspond to residues with Solvent Accessible
 1051     Surface Area (SASA) greater than or equal to the cutoff value. Otherwise, these
 1052     residues are considered as buried residues.
 1053 
 1054     Arguments:
 1055         MoleculeName (str): Name of a PyMOL molecule object.
 1056         ChainName (str): Name of a chain in a molecule.
 1057         SASACutoff (float): SASA cutoff for heavy atoms corresponding to
 1058             surface residues in chain. Units: Angstroms ** 2
 1059 
 1060     Returns:
 1061         dict: A dictionary containing list of residue names and dictionaries of
 1062             residue numbers and residue count for each residue. Names of
 1063             residues in the dictionary are not sorted.
 1064         dict2: Buried residues in a chain.
 1065 
 1066     Examples:
 1067 
 1068         SurfaceResiduesInfo, BurriedResiduesInfo =
 1069             GetSurfaceAndBuriedResiduesInfo(MolName, ChainName, 2.5)
 1070         for ResName in SurfaceResiduesInfo["ResNames"]:
 1071             ResCount = ResiduesInfo["ResCount"][ResName]
 1072             ResNums = ResiduesInfo["ResNum"][ResName]
 1073             MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums: %s" %
 1074                 (ResName, ResCount, ResNums))
 1075 
 1076     """
 1077     if not (len(MoleculeName) and len(ChainName)):
 1078         return None
 1079 
 1080     # Get ready to calculate solvent accessible surface area..
 1081     CurrentDotSolvent = cmd.get("dot_solvent")
 1082     cmd.set("dot_solvent", 1)
 1083 
 1084     # Setup tmp object name...
 1085     TmpChainName = "Tmp_%s" % (MoleculeName)
 1086     TmpChainSelection = "(%s and chain %s and polymer and not hydrogens)" % (MoleculeName, ChainName)
 1087     cmd.create(TmpChainName, "(%s)" % (TmpChainSelection))
 1088 
 1089     # Calculate SASA for chain and load it into b...
 1090     cmd.get_area(TmpChainName, load_b=1)
 1091 
 1092     # Retrieve SASA for residues as B values...
 1093     ResiduesBValuesInfoMap = _GetSelectionResiduesBValuesInfo(TmpChainName)
 1094 
 1095     # Retrieve information for surface and buried residues...
 1096     SurfaceResiduesInfoMap = _GetResiduesInfoFromResiduesBValues(ResiduesBValuesInfoMap, ">=", SASACutoff)
 1097     BuriedResiduesInfoMap = _GetResiduesInfoFromResiduesBValues(ResiduesBValuesInfoMap, "<", SASACutoff)
 1098 
 1099     # Delete tmp objects...
 1100     cmd.delete(TmpChainName)
 1101 
 1102     # Restore current dot solvent...
 1103     cmd.set("dot_solvent", CurrentDotSolvent)
 1104 
 1105     return SurfaceResiduesInfoMap, BuriedResiduesInfoMap
 1106 
 1107 
 1108 def GetInterfaceChainsResiduesByCAlphaAtomsDistance(
 1109     MoleculeName1, ChainNames1, MoleculeName2, ChainNames2, DistanceCutoff=8.0
 1110 ):
 1111     """Get information for interface residues between chains in two
 1112     molecules based on the distance between CAlpha atoms. The chain
 1113     specification for molecules may contain multiple chain names delimited
 1114     by commas.
 1115 
 1116     The interface residues are identified using PyMOL 'bycalpha' selection
 1117     operator with in a specified distance.
 1118 
 1119     Arguments:
 1120         MoleculeName1 (str): Name of a PyMOL molecule object.
 1121         ChainNames1 (str): A chain name or comma delimited list of chain
 1122             names in a molecule.
 1123         MoleculeName2 (str): Name of a PyMOL molecule object.
 1124         ChainNames2 (str): A chain name or comma delimited list of chain
 1125             names in a molecule.
 1126         DistanceCutoff (float): Distance cutoff for distance between
 1127             any two CAlpha atoms in interface residues in different chains.
 1128 
 1129     Returns:
 1130         dict1: Interface residues in a chain for first molecule. It is a
 1131             dictionary containing list of residue names and dictionaries of
 1132             residue numbers and residue count for each residue. Names of
 1133             residues in the dictionary are not sorted.
 1134         dict2: Interface residues in the chain for second molecule.
 1135 
 1136     Examples:
 1137 
 1138         ResiduesInfo1, ResiduesInfo2 =
 1139             GetInterfaceResiduesByHeavyAtomsDistance(MolName1,
 1140             ChainName1, MolName2, ChainName2, DistanceCutoff)
 1141         for ChainID in ResiduesInfo1["ChainIDs"]:
 1142             for ResName in ResiduesInfo1["ResNames"][ChainID]:
 1143                 ResCount = ResiduesInfo1["ResCount"][ChainID][ResName]
 1144                 ResNums = ResiduesInfo1["ResNum"][ChainID][ResName]
 1145                 MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums:
 1146                 %s" % (ResName, ResCount, ResNums))
 1147 
 1148     """
 1149     ChainNames1Selection, ChainNames2Selection = _ValidateAndSetupChainSelectionsForInterfaceResidues(
 1150         MoleculeName1, ChainNames1, MoleculeName2, ChainNames2
 1151     )
 1152 
 1153     if ChainNames1Selection is None or ChainNames2Selection is None:
 1154         return None
 1155 
 1156     MoleculeSelection1 = "%s and chain %s" % (MoleculeName1, ChainNames1Selection)
 1157     MoleculeSelection2 = "%s and chain %s" % (MoleculeName2, ChainNames2Selection)
 1158 
 1159     SelectionCmd1 = "((bycalpha (%s) within %.1f of  (%s)) and polymer)" % (
 1160         MoleculeSelection1,
 1161         DistanceCutoff,
 1162         MoleculeSelection2,
 1163     )
 1164     ResiduesInfoMap1 = _GetSelectionChainsAndResiduesInfo(SelectionCmd1)
 1165 
 1166     SelectionCmd2 = "((bycalpha (%s) within %.1f of  (%s)) and polymer)" % (
 1167         MoleculeSelection2,
 1168         DistanceCutoff,
 1169         MoleculeSelection1,
 1170     )
 1171     ResiduesInfoMap2 = _GetSelectionChainsAndResiduesInfo(SelectionCmd2)
 1172 
 1173     return ResiduesInfoMap1, ResiduesInfoMap2
 1174 
 1175 
 1176 def GetInterfaceChainsResiduesByHeavyAtomsDistance(
 1177     MoleculeName1, ChainNames1, MoleculeName2, ChainNames2, DistanceCutoff=5.0
 1178 ):
 1179     """Get information for interface residues between chains in two
 1180     molecules based on the distance between heavy atoms. The chain
 1181     specification for molecules may contain multiple chain names delimited
 1182     by commas.
 1183 
 1184     The interface residues are identified using PyMOL 'byresidue' selection
 1185     operator with in a specified distance.
 1186 
 1187     Arguments:
 1188         MoleculeName1 (str): Name of a PyMOL molecule object.
 1189         ChainNames1 (str): A chain name or comma delimited list of chain
 1190             names in a molecule.
 1191         MoleculeName2 (str): Name of a PyMOL molecule object.
 1192         ChainNames2 (str): A chain name or comma delimited list of chain
 1193             names in a molecule.
 1194         DistanceCutoff (float): Distance cutoff for distance between
 1195             any two heavy atoms in interface residues in different chains.
 1196 
 1197     Returns:
 1198         dict1: Interface residues in a chain for first molecule. It is a
 1199             dictionary containing list of residue names and dictionaries of
 1200             residue numbers and residue count for each residue. Names of
 1201             residues in the dictionary are not sorted.
 1202         dict2: Interface residues in the chain for second molecule.
 1203 
 1204     Examples:
 1205 
 1206         ResiduesInfo1, ResiduesInfo2 =
 1207             GetInterfaceResiduesByHeavyAtomsDistance(MolName1,
 1208             ChainName1, MolName2, ChainName2, DistanceCutoff)
 1209         for ChainID in ResiduesInfo1["ChainIDs"]:
 1210             for ResName in ResiduesInfo1["ResNames"][ChainID]:
 1211                 ResCount = ResiduesInfo1["ResCount"][ChainID][ResName]
 1212                 ResNums = ResiduesInfo1["ResNum"][ChainID][ResName]
 1213                 MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums:
 1214                 %s" % (ResName, ResCount, ResNums))
 1215 
 1216     """
 1217     ChainNames1Selection, ChainNames2Selection = _ValidateAndSetupChainSelectionsForInterfaceResidues(
 1218         MoleculeName1, ChainNames1, MoleculeName2, ChainNames2
 1219     )
 1220 
 1221     if ChainNames1Selection is None or ChainNames2Selection is None:
 1222         return None
 1223 
 1224     MoleculeSelection1 = "%s and chain %s and (not hydrogens)" % (MoleculeName1, ChainNames1Selection)
 1225     MoleculeSelection2 = "%s and chain %s and (not hydrogens)" % (MoleculeName2, ChainNames2Selection)
 1226 
 1227     SelectionCmd1 = "((byresidue (%s) within %.1f of  (%s)) and polymer)" % (
 1228         MoleculeSelection1,
 1229         DistanceCutoff,
 1230         MoleculeSelection2,
 1231     )
 1232     ResiduesInfoMap1 = _GetSelectionChainsAndResiduesInfo(SelectionCmd1)
 1233 
 1234     SelectionCmd2 = "((byresidue (%s) within %.1f of  (%s)) and polymer)" % (
 1235         MoleculeSelection2,
 1236         DistanceCutoff,
 1237         MoleculeSelection1,
 1238     )
 1239     ResiduesInfoMap2 = _GetSelectionChainsAndResiduesInfo(SelectionCmd2)
 1240 
 1241     return ResiduesInfoMap1, ResiduesInfoMap2
 1242 
 1243 
 1244 def GetInterfaceChainsResiduesBySASAChange(MoleculeName1, ChainNames1, MoleculeName2, ChainNames2, ChangeCutoff=0.75):
 1245     """Get information for interface residues between chains in two
 1246     molecules based on the change in solvent accessible surface area
 1247     (SASA) of a residue in chains in a molecule and chain complex
 1248     containing specified chains across both molecules. The chain
 1249     specification for molecules may contain multiple chain names
 1250     delimited by commas.
 1251 
 1252     Arguments:
 1253         MoleculeName1 (str): Name of a PyMOL molecule object.
 1254         ChainNames1 (str): A chain name or comma delimited list of chain
 1255             names in a molecule.
 1256         MoleculeName2 (str): Name of a PyMOL molecule object.
 1257         ChainNames2 (str): A chain name or comma delimited list of chain
 1258             names in a molecule.
 1259         ChangeCutoff (float): SASA change cutoff for heavy atoms in
 1260             a interface residue between an individual chain and a complex
 1261             containing both chains.  Units: Angstroms ** 2
 1262 
 1263     Returns:
 1264         dict1: Interface residues in the chain for first molecule. It is a
 1265             dictionary containing list of residue names and dictionaries of
 1266             residue numbers and residue count for each residue. Names of
 1267             residues in the dictionary are not sorted.
 1268         dict2: Interface residues in the chain for second molecule.
 1269 
 1270     Examples:
 1271 
 1272         ResiduesInfo1, ResiduesInfo2 =
 1273             GetInterfaceResiduesBySASAChange(MolName1,
 1274             ChainName1, MolName2, ChainName2, DistanceCutoff)
 1275         for ResName in ResiduesInfo1["ResNames"]:
 1276             ResCount = ResiduesInfo1["ResCount"][ResName]
 1277             ResNums = ResiduesInfo1["ResNum"][ResName]
 1278             MiscUtil.PrintInfo("ResName: %s; ResCount: %s; ResNums: %s" %
 1279                 (ResName, ResCount, ResNums))
 1280 
 1281     """
 1282     ChainNames1Selection, ChainNames2Selection = _ValidateAndSetupChainSelectionsForInterfaceResidues(
 1283         MoleculeName1, ChainNames1, MoleculeName2, ChainNames2
 1284     )
 1285 
 1286     if ChainNames1Selection is None or ChainNames2Selection is None:
 1287         return None
 1288 
 1289     # Get ready to calculate solvent accessible surface area..
 1290     CurrentDotSolvent = cmd.get("dot_solvent")
 1291     cmd.set("dot_solvent", 1)
 1292 
 1293     # Setup tmp object names...
 1294     TmpComplexName = "Tmp1_%s_%s" % (MoleculeName1, MoleculeName2)
 1295     TmpChainName1 = "Tmp2_%s" % (MoleculeName1)
 1296     TmpChainName2 = "Tmp3_%s" % (MoleculeName2)
 1297 
 1298     # Setup complex...
 1299     TmpComplexSelection = (
 1300         "(%s and chain %s and polymer and not hydrogens) or (%s and chain %s and polymer and not hydrogens)"
 1301         % (MoleculeName1, ChainNames1Selection, MoleculeName2, ChainNames2Selection)
 1302     )
 1303     cmd.create(TmpComplexName, "(%s)" % (TmpComplexSelection))
 1304 
 1305     # Calculate SASA for complex and load it into b...
 1306     cmd.get_area(TmpComplexName, load_b=1)
 1307     cmd.alter(TmpComplexName, "q=b")
 1308 
 1309     # Setup individual chains and calculate SASA...
 1310     TmpChainNameSelection1 = "%s and (chain %s)" % (TmpComplexName, ChainNames1Selection)
 1311     cmd.extract(TmpChainName1, "(%s)" % TmpChainNameSelection1)
 1312     cmd.get_area(TmpChainName1, load_b=1)
 1313 
 1314     TmpChainNameSelection2 = "%s and (chain %s)" % (TmpComplexName, ChainNames2Selection)
 1315     cmd.extract(TmpChainName2, "(%s)" % TmpChainNameSelection2)
 1316     cmd.get_area(TmpChainName2, load_b=1)
 1317 
 1318     # Calculate SASA difference for individual chains...
 1319     TmpChainNamesSelection = "(%s or %s)" % (TmpChainName1, TmpChainName2)
 1320     cmd.alter(TmpChainNamesSelection, "b=b-q")
 1321 
 1322     ResiduesInfoMap1 = _GetChainsResiduesInfoForSASAChange(TmpChainNamesSelection, TmpChainName1, ChangeCutoff)
 1323     ResiduesInfoMap2 = _GetChainsResiduesInfoForSASAChange(TmpChainNamesSelection, TmpChainName2, ChangeCutoff)
 1324 
 1325     # Delete tmp objects...
 1326     cmd.delete(TmpComplexName)
 1327     cmd.delete(TmpChainName1)
 1328     cmd.delete(TmpChainName2)
 1329 
 1330     # Restore current dot solvent...
 1331     cmd.set("dot_solvent", CurrentDotSolvent)
 1332 
 1333     return ResiduesInfoMap1, ResiduesInfoMap2
 1334 
 1335 
 1336 def _GetChainsResiduesInfoForSASAChange(SelectionCmd, MoleculeName, ChangeCutoff):
 1337     """Get residue info for SASA change."""
 1338 
 1339     # Retrieve data...
 1340     stored.ResiduesInfo = []
 1341     cmd.iterate(SelectionCmd, "stored.ResiduesInfo.append([model, chain, resi, resn, b])")
 1342 
 1343     # Calculate SASA change for each residue...
 1344     SASAChangeMap = {}
 1345     SASAChangeMap["ChainIDs"] = []
 1346     SASAChangeMap["ResIDs"] = {}
 1347     SASAChangeMap["ResSASAChange"] = {}
 1348 
 1349     for Model, ChainID, ResNum, ResName, AtomSASAChange in stored.ResiduesInfo:
 1350         if not re.match(Model, MoleculeName, re.I):
 1351             continue
 1352         ResID = "%s_%s" % (ResName, ResNum)
 1353 
 1354         if ChainID not in SASAChangeMap["ChainIDs"]:
 1355             # Track new chain ID and intialize SASA change information...
 1356             SASAChangeMap["ChainIDs"].append(ChainID)
 1357             SASAChangeMap["ResIDs"][ChainID] = []
 1358             SASAChangeMap["ResSASAChange"][ChainID] = {}
 1359 
 1360         if ResID in SASAChangeMap["ResSASAChange"][ChainID]:
 1361             SASAChangeMap["ResSASAChange"][ChainID][ResID] += AtomSASAChange
 1362         else:
 1363             # Track new residue ID...
 1364             SASAChangeMap["ResIDs"][ChainID].append(ResID)
 1365             SASAChangeMap["ResSASAChange"][ChainID][ResID] = AtomSASAChange
 1366 
 1367     # Identify residues with SASA change greater than the specified cutoff value...
 1368     SelectionInfoMap = {}
 1369     SelectionInfoMap["ChainIDs"] = []
 1370 
 1371     SelectionInfoMap["ResNames"] = {}
 1372     SelectionInfoMap["ResNum"] = {}
 1373     SelectionInfoMap["ResCount"] = {}
 1374 
 1375     for ChainID in SASAChangeMap["ChainIDs"]:
 1376         for ResID in SASAChangeMap["ResIDs"][ChainID]:
 1377             ResSASAChange = SASAChangeMap["ResSASAChange"][ChainID][ResID]
 1378             if abs(ResSASAChange) < ChangeCutoff:
 1379                 continue
 1380             ResName, ResNum = ResID.split("_")
 1381 
 1382             if ChainID not in SelectionInfoMap["ChainIDs"]:
 1383                 # Track new chain ID and intialize residues information...
 1384                 SelectionInfoMap["ChainIDs"].append(ChainID)
 1385                 SelectionInfoMap["ResNames"][ChainID] = []
 1386                 SelectionInfoMap["ResNum"][ChainID] = {}
 1387                 SelectionInfoMap["ResCount"][ChainID] = {}
 1388 
 1389             if ResName in SelectionInfoMap["ResNames"][ChainID]:
 1390                 if ResNum not in SelectionInfoMap["ResNum"][ChainID][ResName]:
 1391                     # Same residue name but different residue number
 1392                     SelectionInfoMap["ResNum"][ChainID][ResName].append(ResNum)
 1393                     SelectionInfoMap["ResCount"][ChainID][ResName] += 1
 1394             else:
 1395                 # Track new residue information...
 1396                 SelectionInfoMap["ResNames"][ChainID].append(ResName)
 1397 
 1398                 SelectionInfoMap["ResNum"][ChainID][ResName] = []
 1399                 SelectionInfoMap["ResNum"][ChainID][ResName].append(ResNum)
 1400 
 1401                 SelectionInfoMap["ResCount"][ChainID][ResName] = 1
 1402 
 1403     return SelectionInfoMap
 1404 
 1405 
 1406 def _ValidateAndSetupChainSelectionsForInterfaceResidues(MoleculeName1, ChainNames1, MoleculeName2, ChainNames2):
 1407     """Validate and setup chains name selectons for idenfitying interface residues."""
 1408 
 1409     ChainNames1 = re.sub(" ", "", ChainNames1)
 1410     ChainNames2 = re.sub(" ", "", ChainNames2)
 1411 
 1412     if not (len(MoleculeName1) and len(ChainNames1) and len(MoleculeName2) and len(ChainNames2)):
 1413         return None, None
 1414 
 1415     # Setup chain names for PyMOL selections...
 1416     ChainNames1Selection = "+".join(ChainNames1.split(","))
 1417     ChainNames2Selection = "+".join(ChainNames2.split(","))
 1418 
 1419     return ChainNames1Selection, ChainNames2Selection
 1420 
 1421 
 1422 def _GetSelectionChainsAndResiduesInfo(SelectionCmd):
 1423     """Get chain names, residue names and count information for a selection."""
 1424 
 1425     # Retrieve atoms...
 1426     stored.SelectionInfo = []
 1427     cmd.iterate(SelectionCmd, "stored.SelectionInfo.append([chain, resi, resn])")
 1428 
 1429     # Retrieve chains and residues...
 1430     SelectionInfoMap = {}
 1431     SelectionInfoMap["ChainIDs"] = []
 1432 
 1433     SelectionInfoMap["ResNames"] = {}
 1434     SelectionInfoMap["ResNum"] = {}
 1435     SelectionInfoMap["ResCount"] = {}
 1436 
 1437     for ChainID, ResNum, ResName in stored.SelectionInfo:
 1438         if ChainID not in SelectionInfoMap["ChainIDs"]:
 1439             # Track new chain ID and intialize residues information...
 1440             SelectionInfoMap["ChainIDs"].append(ChainID)
 1441             SelectionInfoMap["ResNames"][ChainID] = []
 1442             SelectionInfoMap["ResNum"][ChainID] = {}
 1443             SelectionInfoMap["ResCount"][ChainID] = {}
 1444 
 1445         if ResName in SelectionInfoMap["ResNames"][ChainID]:
 1446             if ResNum not in SelectionInfoMap["ResNum"][ChainID][ResName]:
 1447                 # Same residue name but different residue number
 1448                 SelectionInfoMap["ResNum"][ChainID][ResName].append(ResNum)
 1449                 SelectionInfoMap["ResCount"][ChainID][ResName] += 1
 1450         else:
 1451             # Track new residue information...
 1452             SelectionInfoMap["ResNames"][ChainID].append(ResName)
 1453 
 1454             SelectionInfoMap["ResNum"][ChainID][ResName] = []
 1455             SelectionInfoMap["ResNum"][ChainID][ResName].append(ResNum)
 1456 
 1457             SelectionInfoMap["ResCount"][ChainID][ResName] = 1
 1458 
 1459     return SelectionInfoMap
 1460 
 1461 
 1462 def _GetSelectionResiduesInfo(SelectionCmd):
 1463     """Get residue names and count information for a selection."""
 1464 
 1465     # Retrieve atoms...
 1466     stored.SelectionInfo = []
 1467     cmd.iterate(SelectionCmd, "stored.SelectionInfo.append([resi, resn])")
 1468 
 1469     # Retrieve residues...
 1470     SelectionInfoMap = {}
 1471     SelectionInfoMap["ResNames"] = []
 1472     SelectionInfoMap["ResNum"] = {}
 1473     SelectionInfoMap["ResCount"] = {}
 1474 
 1475     for ResNum, ResName in stored.SelectionInfo:
 1476         if ResName in SelectionInfoMap["ResNames"]:
 1477             if ResNum not in SelectionInfoMap["ResNum"][ResName]:
 1478                 # Same residue name but different residue number
 1479                 SelectionInfoMap["ResNum"][ResName].append(ResNum)
 1480                 SelectionInfoMap["ResCount"][ResName] += 1
 1481         else:
 1482             SelectionInfoMap["ResNames"].append(ResName)
 1483 
 1484             SelectionInfoMap["ResNum"][ResName] = []
 1485             SelectionInfoMap["ResNum"][ResName].append(ResNum)
 1486 
 1487             SelectionInfoMap["ResCount"][ResName] = 1
 1488 
 1489     return SelectionInfoMap
 1490 
 1491 
 1492 def _GetSelectionPhiPsiResiduesInfo(SelectionCmd, Categorize):
 1493     """Get phi and psi torsion angle information for residues in a  selection
 1494     with in a chain.
 1495 
 1496     The phi and psi angles are optionally categorized into the following groups
 1497     corresponding to four types of  Ramachandran plots:
 1498 
 1499     General: All residues except glycine, proline, or pre-proline
 1500     Glycine: Only glycine residues
 1501     Proline: Only proline residues
 1502     Pre-Proline: Only residues before proline not including glycine or proline
 1503 
 1504     """
 1505 
 1506     # Initialize...
 1507     SelectionInfoMap = {}
 1508     SelectionInfoMap["ResNums"] = []
 1509     SelectionInfoMap["ResName"] = {}
 1510     SelectionInfoMap["Phi"] = {}
 1511     SelectionInfoMap["Psi"] = {}
 1512     SelectionInfoMap["Category"] = {}
 1513 
 1514     # Retrieve phi and psi info...
 1515     PhiPsiInfo = _GetSelectionPhiPsiInfo(SelectionCmd, Categorize)
 1516     if PhiPsiInfo is None:
 1517         return SelectionInfoMap
 1518 
 1519     # Track...
 1520     for Model, ChainID, ResNum, ResName, Phi, Psi, Category in PhiPsiInfo:
 1521         if ResNum in SelectionInfoMap["ResName"]:
 1522             continue
 1523 
 1524         SelectionInfoMap["ResNums"].append(ResNum)
 1525         SelectionInfoMap["ResName"][ResNum] = ResName
 1526         SelectionInfoMap["Phi"][ResNum] = Phi
 1527         SelectionInfoMap["Psi"][ResNum] = Psi
 1528         SelectionInfoMap["Category"][ResNum] = Category
 1529 
 1530     return SelectionInfoMap
 1531 
 1532 
 1533 def _GetSelectionPhiPsiChainsAndResiduesInfo(SelectionCmd, Categorize):
 1534     """Get phi and psi torsion angle information for residues in a  selection
 1535     across chains in a molecule.
 1536 
 1537     The phi and psi angles are optionally categorized into the following groups
 1538     corresponding to four types of  Ramachandran plots:
 1539 
 1540     General: All residues except glycine, proline, or pre-proline
 1541     Glycine: Only glycine residues
 1542     Proline: Only proline residues
 1543     Pre-Proline: Only residues before proline not including glycine or proline
 1544 
 1545     """
 1546 
 1547     # Initialize...
 1548     SelectionInfoMap = {}
 1549     SelectionInfoMap["ChainIDs"] = []
 1550     SelectionInfoMap["ResNums"] = {}
 1551     SelectionInfoMap["ResName"] = {}
 1552     SelectionInfoMap["Phi"] = {}
 1553     SelectionInfoMap["Psi"] = {}
 1554     SelectionInfoMap["Category"] = {}
 1555 
 1556     # Retrieve phi and psi info...
 1557     PhiPsiInfo = _GetSelectionPhiPsiInfo(SelectionCmd, Categorize)
 1558     if PhiPsiInfo is None:
 1559         return SelectionInfoMap
 1560 
 1561     # Track...
 1562     for Model, ChainID, ResNum, ResName, Phi, Psi, Category in PhiPsiInfo:
 1563         if ChainID not in SelectionInfoMap["ResNums"]:
 1564             # Track new chain ID and initialize residues information...
 1565             SelectionInfoMap["ChainIDs"].append(ChainID)
 1566 
 1567             SelectionInfoMap["ResNums"][ChainID] = []
 1568             SelectionInfoMap["ResName"][ChainID] = {}
 1569             SelectionInfoMap["Phi"][ChainID] = {}
 1570             SelectionInfoMap["Psi"][ChainID] = {}
 1571             SelectionInfoMap["Category"][ChainID] = {}
 1572 
 1573         # Check for duplicates...
 1574         if ResNum in SelectionInfoMap["ResName"][ChainID]:
 1575             continue
 1576 
 1577         # Track new residues information...
 1578         SelectionInfoMap["ResNums"][ChainID].append(ResNum)
 1579         SelectionInfoMap["ResName"][ChainID][ResNum] = ResName
 1580         SelectionInfoMap["Phi"][ChainID][ResNum] = Phi
 1581         SelectionInfoMap["Psi"][ChainID][ResNum] = Psi
 1582         SelectionInfoMap["Category"][ChainID][ResNum] = Category
 1583 
 1584     return SelectionInfoMap
 1585 
 1586 
 1587 def _GetSelectionPhiPsiCategoriesResiduesInfo(SelectionCmd):
 1588     """Get phi and psi torsion angle information for residues in a  selection
 1589     with in a chain.
 1590 
 1591     The phi and psi angles are optionally categorized into the following groups
 1592     corresponding to four types of  Ramachandran plots:
 1593 
 1594     General: All residues except glycine, proline, or pre-proline
 1595     Glycine: Only glycine residues
 1596     Proline: Only proline residues
 1597     Pre-Proline: Only residues before proline not including glycine or proline
 1598 
 1599     """
 1600 
 1601     # Initialize...
 1602     PhiPsiInfoMaps = {}
 1603     PhiPsiInfoMaps["Maps"] = []
 1604     for Category in ["General", "Glycine", "Proline", "Pre-Proline"]:
 1605         PhiPsiInfoMap = {}
 1606         PhiPsiInfoMap["ResNums"] = []
 1607         PhiPsiInfoMap["ResName"] = {}
 1608         PhiPsiInfoMap["Phi"] = {}
 1609         PhiPsiInfoMap["Psi"] = {}
 1610         PhiPsiInfoMap["Category"] = {}
 1611 
 1612         PhiPsiInfoMaps[Category] = PhiPsiInfoMap
 1613         PhiPsiInfoMaps["Maps"].append(PhiPsiInfoMap)
 1614 
 1615     # Retrieve phi and psi info...
 1616     PhiPsiInfo = _GetSelectionPhiPsiInfo(SelectionCmd, True)
 1617     if PhiPsiInfo is None:
 1618         return PhiPsiInfoMaps["Maps"]
 1619 
 1620     Index = -1
 1621     for Model, ChainID, ResNum, ResName, Phi, Psi, Category in PhiPsiInfo:
 1622         Index += 1
 1623 
 1624         if ResNum in PhiPsiInfoMaps[Category]["ResName"]:
 1625             continue
 1626 
 1627         # Track...
 1628         PhiPsiInfoMaps[Category]["ResNums"].append(ResNum)
 1629         PhiPsiInfoMaps[Category]["ResName"][ResNum] = ResName
 1630         PhiPsiInfoMaps[Category]["Phi"][ResNum] = Phi
 1631         PhiPsiInfoMaps[Category]["Psi"][ResNum] = Psi
 1632         PhiPsiInfoMaps[Category]["Category"][ResNum] = Category
 1633 
 1634     return PhiPsiInfoMaps["Maps"]
 1635 
 1636 
 1637 def _GetSelectionPhiPsiInfo(SelectionCmd, Categorize=False):
 1638     """Get phi and psi angles for a selection along with information regarding
 1639     model, chain, residue number, and residue name.
 1640 
 1641     The phi and psi angles are optionally categorized into the following groups
 1642     corresponding to four types of  Ramachandran plots:
 1643 
 1644     General: All residues except glycine, proline, or pre-proline
 1645     Glycine: Only glycine residues
 1646     Proline: Only proline residues
 1647     Pre-Proline: Only residues before proline not including glycine or proline
 1648 
 1649     """
 1650 
 1651     # Retrieve phi and psi values...
 1652     PhiPsiInfoMap = cmd.get_phipsi("(%s)" % SelectionCmd)
 1653     if PhiPsiInfoMap is None:
 1654         return None
 1655 
 1656     # Sort keys corresponding to model name and residue numbers...
 1657     PhiPsiKeys = sorted(PhiPsiInfoMap.keys())
 1658 
 1659     # Retrieve related information...
 1660     PhiPsiInfoList = []
 1661 
 1662     Category = None
 1663     for Key in PhiPsiKeys:
 1664         Model, Index = Key
 1665         Phi, Psi = PhiPsiInfoMap[Key]
 1666 
 1667         stored.PhiPsiInfoList = []
 1668         cmd.iterate("(%s`%d)" % (Model, Index), "stored.PhiPsiInfoList.append([model, chain, resi, resn])")
 1669         for Model, Chain, ResNum, ResName in stored.PhiPsiInfoList:
 1670             PhiPsiInfoList.append([Model, Chain, ResNum, ResName, Phi, Psi, Category])
 1671 
 1672     if Categorize:
 1673         _CategorizePhiPsiAnglesInfo(PhiPsiInfoList)
 1674 
 1675     return PhiPsiInfoList
 1676 
 1677 
 1678 def _CategorizePhiPsiAnglesInfo(PhiPsiInfo):
 1679     """The phi and psi angles are optionally categorized into the following groups
 1680     corresponding to four types of  Ramachandran plots:
 1681 
 1682     General: All residues except glycine, proline, or pre-proline
 1683     Glycine: Only glycine residues
 1684     Proline: Only proline residues
 1685     Pre-Proline: Only residues before proline not including glycine or proline
 1686 
 1687     """
 1688 
 1689     Index = -1
 1690     LastIndex = len(PhiPsiInfo) - 1
 1691     PhiPsiCategories = []
 1692     for Model, ChainID, ResNum, ResName, Phi, Psi, CategoryPlaceHolder in PhiPsiInfo:
 1693         Index += 1
 1694         if re.match("^Gly$", ResName, re.I):
 1695             # Gly: Only glycine residues
 1696             Category = "Glycine"
 1697         elif re.match("^Pro$", ResName, re.I):
 1698             # Pro: Only proline residues
 1699             Category = "Proline"
 1700         elif _IsPreProlinePhiPsiAngle(PhiPsiInfo, Index, LastIndex):
 1701             #  Pre-Proline: Only residues before proline not including glycine or proline
 1702             Category = "Pre-Proline"
 1703         else:
 1704             # General: All residues except Gly, Pro, or pre-Pro
 1705             Category = "General"
 1706         # Track categories...
 1707         PhiPsiCategories.append(Category)
 1708 
 1709     # Update category in PhiPsiInfo...
 1710     CategoryIndex = 6
 1711     for Index, Category in enumerate(PhiPsiCategories):
 1712         PhiPsiInfo[Index][CategoryIndex] = Category
 1713 
 1714 
 1715 def _IsPreProlinePhiPsiAngle(PhiPsiInfo, Index, LastIndex):
 1716     """Check for Pre-Proline phi and psi angles."""
 1717 
 1718     #  Pre-Proline: Only residues before proline not including glycine or proline
 1719 
 1720     # Not a last residue...
 1721     NextIndex = Index + 1
 1722     if NextIndex >= LastIndex:
 1723         return False
 1724 
 1725     # Next residue is proline...
 1726     if not re.match("^Pro$", PhiPsiInfo[NextIndex][3], re.I):
 1727         return False
 1728 
 1729     # Current residue is not Gly or Pro...
 1730     NextResName = PhiPsiInfo[Index][3]
 1731     if re.match("^(Gly|Pro)$", NextResName, re.I):
 1732         return False
 1733 
 1734     # Next chain ID is same as current chain ID...
 1735     CurrentChainID = PhiPsiInfo[Index][1]
 1736     NextChainID = PhiPsiInfo[NextIndex][1]
 1737     if not re.match("^%s$" % CurrentChainID, NextChainID):
 1738         return False
 1739 
 1740     # Current and next residue numbers are sequential...
 1741     CurrentResNum = int(PhiPsiInfo[Index][2])
 1742     NextResNum = int(PhiPsiInfo[NextIndex][2])
 1743     if not (NextResNum - CurrentResNum == 1):
 1744         return False
 1745 
 1746     return True
 1747 
 1748 
 1749 def _GetSelectionResiduesBValuesInfo(SelectionCmd):
 1750     """Get B values info for residues in a chain."""
 1751 
 1752     # Retrieve data...
 1753     stored.ResiduesInfo = []
 1754     cmd.iterate(SelectionCmd, "stored.ResiduesInfo.append([resi, resn, b])")
 1755 
 1756     # Setup B-values for each residue...
 1757     BValuesInfoMap = {}
 1758     BValuesInfoMap["ResIDs"] = []
 1759     BValuesInfoMap["BValue"] = {}
 1760 
 1761     for ResNum, ResName, AtomBValue in stored.ResiduesInfo:
 1762         ResID = "%s_%s" % (ResName, ResNum)
 1763 
 1764         if ResID in BValuesInfoMap["BValue"]:
 1765             BValuesInfoMap["BValue"][ResID] += AtomBValue
 1766         else:
 1767             # Track new residue ID...
 1768             BValuesInfoMap["ResIDs"].append(ResID)
 1769             BValuesInfoMap["BValue"][ResID] = AtomBValue
 1770 
 1771     return BValuesInfoMap
 1772 
 1773 
 1774 def _GetResiduesInfoFromResiduesBValues(ResiduesBValuesInfoMap, Mode, Cutoff):
 1775     """Get residues info map using residues B values."""
 1776 
 1777     GreaterThanEquals, GreaterThan, LessThanEquals, LessThan, Equals = [False] * 5
 1778     if re.match("^>=$", Mode, re.I):
 1779         GreaterThanEquals = True
 1780     elif re.match("^>$", Mode, re.I):
 1781         GreaterThan = True
 1782     elif re.match("^<=$", Mode, re.I):
 1783         LessThanEquals = True
 1784     elif re.match("^<$", Mode, re.I):
 1785         LessThan = True
 1786     elif re.match("^=$", Mode, re.I):
 1787         Equals = True
 1788     else:
 1789         MiscUtil.PrintError(
 1790             "PyMOLUtil._GetResiduesInfoFromResiduesBValues: The value, %s, specified for Mode is not valid. Supported values: >=, >, <=, <, ="
 1791         )
 1792 
 1793     # Setup residues info map...
 1794     ResiduesInfoMap = {}
 1795     ResiduesInfoMap["ResNames"] = []
 1796     ResiduesInfoMap["ResNum"] = {}
 1797     ResiduesInfoMap["ResCount"] = {}
 1798 
 1799     for ResID in ResiduesBValuesInfoMap["ResIDs"]:
 1800         ResName, ResNum = ResID.split("_")
 1801         ResBValue = ResiduesBValuesInfoMap["BValue"][ResID]
 1802 
 1803         if GreaterThanEquals:
 1804             if ResBValue < Cutoff:
 1805                 continue
 1806         elif GreaterThan:
 1807             if ResBValue <= Cutoff:
 1808                 continue
 1809         elif LessThanEquals:
 1810             if ResBValue > Cutoff:
 1811                 continue
 1812         elif LessThan:
 1813             if ResBValue >= Cutoff:
 1814                 continue
 1815         elif Equals:
 1816             if ResBValue != Cutoff:
 1817                 continue
 1818 
 1819         if ResName in ResiduesInfoMap["ResNames"]:
 1820             if ResNum not in ResiduesInfoMap["ResNum"][ResName]:
 1821                 # Same residue name but different residue number
 1822                 ResiduesInfoMap["ResNum"][ResName].append(ResNum)
 1823                 ResiduesInfoMap["ResCount"][ResName] += 1
 1824         else:
 1825             ResiduesInfoMap["ResNames"].append(ResName)
 1826 
 1827             ResiduesInfoMap["ResNum"][ResName] = []
 1828             ResiduesInfoMap["ResNum"][ResName].append(ResNum)
 1829 
 1830             ResiduesInfoMap["ResCount"][ResName] = 1
 1831 
 1832     return ResiduesInfoMap
 1833 
 1834 
 1835 def ProcessChainsAndLigandsOptionsInfo(
 1836     ChainsAndLigandsInfo, ChainsOptionName, ChainsOptionValue, LigandsOptionName=None, LigandsOptionValue=None
 1837 ):
 1838     """Process specified chain and ligand IDs using command line options.
 1839 
 1840     Arguments:
 1841         ChainsAndLigandsInfo (dict): A dictionary containing information
 1842             existing chains and ligands.
 1843         ChainsOptionName (str): Name of command line chains option.
 1844         ChainsOptionValue (str): Value for command line chains option.
 1845         LigandsOptionName (str): Name of command line ligands option.
 1846         LigandsOptionValue (str): Value for command line ligands option.
 1847 
 1848     Returns:
 1849         dict: A dictionary containing list of chain identifiers and dictionaries
 1850             of chains containing lists of ligand names for each chain.
 1851 
 1852     Examples:
 1853 
 1854         ChainsAndLigandsInfo = ProcessChainsAndLigandsOptionsInfo(
 1855             ChainsAndLigandsInfo, "-c, --chainIDs", OptionsInfo["ChainIDs"],
 1856             "-l, --ligandIDs", OptionsInfo["LigandIDs"])
 1857         for ChainID in ChainsAndLigandsInfo["ChainIDs"]:
 1858             for LigandID in ChainsAndLigandsInfo["LigandIDs"][ChainID]:
 1859                 MiscUtil.PrintInfo("ChainID: %s; LigandID: %s" % (ChainID,
 1860                     LigandID))
 1861 
 1862     """
 1863     SpecifiedChainsAndLigandsInfo = {}
 1864     SpecifiedChainsAndLigandsInfo["ChainIDs"] = []
 1865     SpecifiedChainsAndLigandsInfo["LigandIDs"] = {}
 1866 
 1867     if ChainsOptionValue is None:
 1868         return SpecifiedChainsAndLigandsInfo
 1869 
 1870     _ProcessChainIDs(ChainsAndLigandsInfo, SpecifiedChainsAndLigandsInfo, ChainsOptionName, ChainsOptionValue)
 1871 
 1872     if LigandsOptionValue is None:
 1873         return SpecifiedChainsAndLigandsInfo
 1874 
 1875     _ProcessLigandIDs(ChainsAndLigandsInfo, SpecifiedChainsAndLigandsInfo, LigandsOptionName, LigandsOptionValue)
 1876 
 1877     return SpecifiedChainsAndLigandsInfo
 1878 
 1879 
 1880 def _ProcessChainIDs(ChainsAndLigandsInfo, SpecifiedChainsAndLigandsInfo, ChainsOptionName, ChainsOptionValue):
 1881     """Process chain IDs"""
 1882 
 1883     MiscUtil.PrintInfo("Processing chain IDs...")
 1884 
 1885     if re.match("^All$", ChainsOptionValue, re.I):
 1886         SpecifiedChainsAndLigandsInfo["ChainIDs"] = ChainsAndLigandsInfo["ChainIDs"]
 1887         return
 1888     elif re.match("^(First|Auto)$", ChainsOptionValue, re.I):
 1889         FirstChainID = ChainsAndLigandsInfo["ChainIDs"][0] if (len(ChainsAndLigandsInfo["ChainIDs"])) else None
 1890         if FirstChainID is not None:
 1891             SpecifiedChainsAndLigandsInfo["ChainIDs"].append(FirstChainID)
 1892         return
 1893 
 1894     ChainIDs = re.sub(" ", "", ChainsOptionValue)
 1895     if not ChainIDs:
 1896         MiscUtil.PrintError('No valid value specified using "%s" option.' % ChainsOptionName)
 1897 
 1898     ChainIDsList = ChainsAndLigandsInfo["ChainIDs"]
 1899     SpecifiedChainIDsList = []
 1900 
 1901     ChainIDsWords = ChainIDs.split(",")
 1902     for ChainID in ChainIDsWords:
 1903         if ChainID not in ChainIDsList:
 1904             MiscUtil.PrintWarning(
 1905                 'The chain ID, %s, specified using "%s" option is not valid. It\'ll be ignored. Valid chain IDs: %s'
 1906                 % (ChainID, ChainsOptionName, ", ".join(ChainIDsList))
 1907             )
 1908             continue
 1909         if ChainID in SpecifiedChainIDsList:
 1910             MiscUtil.PrintWarning(
 1911                 'The chain ID, %s, has already been specified using "%s" option. It\'ll be ignored.'
 1912                 % (ChainID, ChainsOptionName)
 1913             )
 1914             continue
 1915         SpecifiedChainIDsList.append(ChainID)
 1916 
 1917     if not len(SpecifiedChainIDsList):
 1918         MiscUtil.PrintError(
 1919             'No valid chain IDs "%s"  specified using "%s" option.' % (ChainsOptionValue, ChainsOptionName)
 1920         )
 1921 
 1922     SpecifiedChainsAndLigandsInfo["ChainIDs"] = SpecifiedChainIDsList
 1923 
 1924 
 1925 def _ProcessLigandIDs(ChainsAndLigandsInfo, SpecifiedChainsAndLigandsInfo, LigandsOptionName, LigandsOptionValue):
 1926     """Process ligand IDs"""
 1927 
 1928     MiscUtil.PrintInfo("Processing ligand IDs...")
 1929 
 1930     # Intialize ligand IDs...
 1931     for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
 1932         SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID] = []
 1933 
 1934     if re.match("^All$", LigandsOptionValue, re.I):
 1935         for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
 1936             SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID] = ChainsAndLigandsInfo["LigandIDs"][ChainID]
 1937         return
 1938     elif re.match("^(Largest|Auto)$", LigandsOptionValue, re.I):
 1939         # Setup largest ligand ID for each chain...
 1940         for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
 1941             LargestLigandID = (
 1942                 ChainsAndLigandsInfo["LigandIDs"][ChainID][0]
 1943                 if (len(ChainsAndLigandsInfo["LigandIDs"][ChainID]))
 1944                 else None
 1945             )
 1946             if LargestLigandID is not None:
 1947                 SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID].append(LargestLigandID)
 1948         return
 1949 
 1950     LigandIDs = re.sub(" ", "", LigandsOptionValue)
 1951     if not LigandIDs:
 1952         MiscUtil.PrintError('No valid value specified using "%s" option.' % LigandsOptionName)
 1953 
 1954     LigandIDsWords = LigandIDs.split(",")
 1955 
 1956     for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
 1957         LigandIDsList = ChainsAndLigandsInfo["LigandIDs"][ChainID]
 1958         SpecifiedLigandIDsList = []
 1959 
 1960         for LigandID in LigandIDsWords:
 1961             if LigandID not in LigandIDsList:
 1962                 LigandIDsListNames = ",".join(LigandIDsList) if len(LigandIDsList) else "None"
 1963                 MiscUtil.PrintWarning(
 1964                     'The ligand ID, %s, specified using "%s" option is not valid for chain, %s. It\'ll be ignored. Valid ligand IDs: %s'
 1965                     % (LigandID, LigandsOptionName, ChainID, LigandIDsListNames)
 1966                 )
 1967                 continue
 1968             if LigandID in SpecifiedLigandIDsList:
 1969                 MiscUtil.PrintWarning(
 1970                     'The ligand ID, %s, has already been specified using "%s" option. It\'ll be ignored.'
 1971                     % (LigandID, LigandsOptionName)
 1972                 )
 1973                 continue
 1974             SpecifiedLigandIDsList.append(LigandID)
 1975 
 1976         if not len(SpecifiedLigandIDsList):
 1977             MiscUtil.PrintWarning(
 1978                 'No valid ligand IDs "%s" specified using "%s" option for chain ID, %s.'
 1979                 % (LigandsOptionValue, LigandsOptionName, ChainID)
 1980             )
 1981 
 1982         SpecifiedChainsAndLigandsInfo["LigandIDs"][ChainID] = SpecifiedLigandIDsList
 1983 
 1984 
 1985 def ProcessResidueTypesOptionsInfo(ResidueTypesOptionName, ResidueTypesOptionValue):
 1986     """Process specified residue types using command line option.
 1987 
 1988     Arguments:
 1989         ResidueTypesOptionName (str): Name of command line  option.
 1990         ResidueTypesOptionValue (str): Value for command line option.
 1991 
 1992     Returns:
 1993         list: A list containing names of valid residue types.
 1994         dict: A dictionary containing residue types pointing to dictionaries of
 1995             color names and list of residues for a residue type.
 1996 
 1997     Examples:
 1998 
 1999         ResidueTypesNamesInfo, ResidueTypesParamsInfo =
 2000             ProcessChainsAndLigandsOptionsInfo("-r, --residueTypes",
 2001             OptionsInfo["ResidueTypes"])
 2002         for ResidueTypeName in ResidueTypesNamesInfo:
 2003             MiscUtil.PrintInfo("ResidueType: %s; Color: %s; Residues: %s" %
 2004             (ResidueTypeName, ResidueTypeName[ResidueTypeName]["Color"],
 2005             " ".join(ResidueTypeName[ResidueTypeName]["Residues"]))
 2006 
 2007     """
 2008 
 2009     # Set up  default values for residue types, colors, and names.
 2010     ResidueTypesNamesInfo = ["Aromatic", "Hydrophobic", "Polar", "Positively_Charged", "Negatively_Charged"]
 2011 
 2012     ResidueTypesParamsInfo = {}
 2013     ResidueTypesParamsInfo["Aromatic"] = {"Color": "brightorange", "Residues": ["HIS", "PHE", "TRP", "TYR"]}
 2014     ResidueTypesParamsInfo["Hydrophobic"] = {
 2015         "Color": "orange",
 2016         "Residues": ["ALA", "GLY", "VAL", "LEU", "ILE", "PRO", "MET"],
 2017     }
 2018     ResidueTypesParamsInfo["Polar"] = {"Color": "palegreen", "Residues": ["ASN", "GLN", "SER", "THR", "CYS"]}
 2019     ResidueTypesParamsInfo["Positively_Charged"] = {"Color": "marine", "Residues": ["ARG", "LYS"]}
 2020     ResidueTypesParamsInfo["Negatively_Charged"] = {"Color": "red", "Residues": ["ASP", "GLU"]}
 2021 
 2022     ResidueTypes = ResidueTypesOptionValue
 2023     if re.match("^auto$", ResidueTypesOptionValue, re.I):
 2024         _SetupOtherResidueTypes(ResidueTypesParamsInfo)
 2025         return ResidueTypesNamesInfo, ResidueTypesParamsInfo
 2026 
 2027     # Parse specified residue types...
 2028     ResidueTypesWords = ResidueTypes.split(",")
 2029     if len(ResidueTypesWords) % 3:
 2030         MiscUtil.PrintError(
 2031             'The number of comma delimited residue type, color, and name triplets, %d, specified using "%s" option must be a multple of 3.'
 2032             % (len(ResidueTypesWords), ResidueTypesOptionName)
 2033         )
 2034 
 2035     # Set up canonical residue type names...
 2036     ValidResidueTypeNames = []
 2037     CanonicalResidueTypeNamesMap = {}
 2038     for Name in sorted(ResidueTypesNamesInfo):
 2039         ValidResidueTypeNames.append(Name)
 2040         CanonicalResidueTypeNamesMap[Name.lower()] = Name
 2041 
 2042     # Validate and set residue types, colors, and names..
 2043     for Index in range(0, len(ResidueTypesWords), 3):
 2044         TypeName = ResidueTypesWords[Index].strip()
 2045         ResidueTypeColor = ResidueTypesWords[Index + 1].strip()
 2046         ResidueNames = ResidueTypesWords[Index + 2].strip()
 2047 
 2048         ResidueNames = re.sub("[ ]+", " ", ResidueNames)
 2049         ResidueNamesWords = ResidueNames.split(" ")
 2050 
 2051         CanonicalTypeName = TypeName.lower()
 2052         if CanonicalTypeName not in CanonicalResidueTypeNamesMap:
 2053             MiscUtil.PrintError(
 2054                 'The residue type, %s, specified using "%s" option is not a valid type. Supported residue types: %s'
 2055                 % (TypeName, ResidueTypesOptionName, ", ".join(ValidResidueTypeNames))
 2056             )
 2057         ResidueTypeName = CanonicalResidueTypeNamesMap[CanonicalTypeName]
 2058 
 2059         if not ResidueTypeColor:
 2060             MiscUtil.PrintError(
 2061                 'No color name specified for residue type, %s, using "%s" option, %s'
 2062                 % (TypeName, ResidueTypesOptionName, ResidueTypes)
 2063             )
 2064 
 2065         if not ResidueNames:
 2066             MiscUtil.PrintError(
 2067                 'No residue names specified for residue type, %s, using "%s" option, %s'
 2068                 % (TypeName, ResidueTypesOptionName, ResidueTypes)
 2069             )
 2070 
 2071         ResidueTypesParamsInfo[ResidueTypeName]["Color"] = ResidueTypeColor
 2072         ResidueTypesParamsInfo[ResidueTypeName]["Residues"] = ResidueNamesWords
 2073 
 2074         _SetupOtherResidueTypes()
 2075         return ResidueTypesNamesInfo, ResidueTypesParamsInfo
 2076 
 2077 
 2078 def _SetupOtherResidueTypes(ResidueTypesParamsInfo):
 2079     """Setup other residue types."""
 2080 
 2081     # Set other residues to all specified residues. The other residues are selected by
 2082     # performing a negation operation on this list during the creation of PyMOL objects.
 2083 
 2084     ResidueNames = []
 2085     for ResiduesType in ResidueTypesParamsInfo:
 2086         ResidueNames.extend(ResidueTypesParamsInfo[ResiduesType]["Residues"])
 2087 
 2088     ResidueTypesParamsInfo["Other"] = {}
 2089     ResidueTypesParamsInfo["Other"]["Color"] = None
 2090     ResidueTypesParamsInfo["Other"]["Residues"] = ResidueNames
 2091 
 2092 
 2093 def ProcessSurfaceAtomTypesColorsOptionsInfo(ColorOptionName, ColorOptionValue):
 2094     """Process specified surafce atom types colors using command line option.
 2095 
 2096     Arguments:
 2097         ColorOptionName (str): Name of command line  option.
 2098         ColorOptionValue (str): Value for command line option.
 2099 
 2100     Returns:
 2101         dict: A dictionary containing atom types and colors.
 2102 
 2103     Examples:
 2104 
 2105         AtomTypesColorNamesInfo =
 2106             PyMOLUtil.ProcessSurfaceAtomTypesColorsOptionsInfo(
 2107             "--surfaceAtomTypesColors", OptionsInfo["SurfaceAtomTypesColors"])
 2108 
 2109     """
 2110 
 2111     # Set up  default values for atom type colors...
 2112     AtomTypesColorNamesInfo = {
 2113         "HydrophobicAtomsColor": "yellow",
 2114         "NegativelyChargedAtomsColor": "red",
 2115         "PositivelyChargedAtomsColor": "blue",
 2116         "OtherAtomsColor": "gray90",
 2117     }
 2118 
 2119     AtomTypesColors = ColorOptionValue
 2120     if re.match("^auto$", AtomTypesColors, re.I):
 2121         return AtomTypesColorNamesInfo
 2122 
 2123     # Parse atom type colors and values...
 2124     AtomTypesColorsWords = AtomTypesColors.split(",")
 2125     if len(AtomTypesColorsWords) % 2:
 2126         MiscUtil.PrintError(
 2127             'The number of comma delimited surface atom color types and values, %d, specified using "%s" option must be a multple of 2.'
 2128             % (len(AtomTypesColorsWords), ColorOptionName)
 2129         )
 2130 
 2131     # Set up canonical atom type colors...
 2132     ValidAtomTypesColors = []
 2133     CanonicalAtomTypesColorsMap = {}
 2134     for Type in sorted(AtomTypesColorNamesInfo):
 2135         ValidAtomTypesColors.append(Type)
 2136         CanonicalAtomTypesColorsMap[Type.lower()] = Type
 2137 
 2138     # Validate and process specified values...
 2139     for Index in range(0, len(AtomTypesColorsWords), 2):
 2140         Type = AtomTypesColorsWords[Index].strip()
 2141         Color = AtomTypesColorsWords[Index + 1].strip()
 2142 
 2143         Color = re.sub("[ ]+", " ", Color)
 2144         ColorWords = Color.split(" ")
 2145 
 2146         CanonicalType = Type.lower()
 2147         if CanonicalType not in CanonicalAtomTypesColorsMap:
 2148             MiscUtil.PrintError(
 2149                 'The surface atom color type, %s, specified using "%s" option  is not a valid type: Supported atom color types: %s'
 2150                 % (Type, ColorOptionName, ", ".join(ValidAtomTypesColors))
 2151             )
 2152         Type = CanonicalAtomTypesColorsMap[CanonicalType]
 2153 
 2154         if not Color:
 2155             MiscUtil.PrintError(
 2156                 'No color type specified for atom color type, %s, using "%s" option.' % (Type, ColorOptionName)
 2157             )
 2158 
 2159         ColorWordsLen = len(ColorWords)
 2160         if not (ColorWordsLen == 1 or ColorWordsLen == 3):
 2161             MiscUtil.PrintError(
 2162                 'The number, %s, of color name or space delimited RGB values, %s, specified for atom color type, %s, using "%s" option must be 1 or 3.'
 2163                 % (ColorWordsLen, Color, ColorOptionName, Type)
 2164             )
 2165 
 2166         AtomTypesColorNamesInfo[Type] = " ".join(ColorWords)
 2167 
 2168         return AtomTypesColorNamesInfo
 2169 
 2170 
 2171 def ProcessSaltBridgesChainResiduesOptionsInfo(SaltBridgesOptionName, SaltBridgesOptionValue):
 2172     """Process specified salt bridges chain residues using command line option.
 2173 
 2174     Arguments:
 2175         SaltBridgesOptionName (str): Name of command line  option.
 2176         SaltBridgesOptionValue (str): Value for command line option.
 2177 
 2178     Returns:
 2179         dict: A dictionary containing salt bridges residue types and residue
 2180             names.
 2181 
 2182     Examples:
 2183 
 2184         SaltBridgesChainResiduesInfo =
 2185             PyMOLUtil.ProcessSaltBridgesChainResiduesOptionsInfo(
 2186             "--saltBridgesChainResidues", OptionsInfo["SaltBridgesChainResidues"])
 2187 
 2188     """
 2189 
 2190     # Set up  default values for salt bridges chain residues...
 2191     SaltBridgesChainResiduesInfo = {
 2192         "Positively_Charged": ["ARG", "LYS", "HIS", "HSP"],
 2193         "Negatively_Charged": ["ASP", "GLU"],
 2194     }
 2195 
 2196     SaltBridgesChainResidues = SaltBridgesOptionValue
 2197     if re.match("^auto$", SaltBridgesChainResidues, re.I):
 2198         return SaltBridgesChainResiduesInfo
 2199 
 2200     # Parse salt bridges chain residues types and residue names...
 2201     SaltBridgesChainResiduesWords = SaltBridgesChainResidues.split(",")
 2202     if len(SaltBridgesChainResiduesWords) % 2:
 2203         MiscUtil.PrintError(
 2204             'The number of comma delimited salt bridges residue types and names, %d, specified using "%s" option must be a multple of 2.'
 2205             % (len(SaltBridgesChainResiduesWords), SaltBridgesOptionName)
 2206         )
 2207 
 2208     # Set up canonical salt bridges chain residue types...
 2209     ValidSaltBridgesChainResidueTypes = []
 2210     CanonicalSaltBridgesChainResiduesMap = {}
 2211     for Type in sorted(SaltBridgesChainResiduesInfo):
 2212         ValidSaltBridgesChainResidueTypes.append(Type)
 2213         CanonicalSaltBridgesChainResiduesMap[Type.lower()] = Type
 2214 
 2215     # Validate and process specified values...
 2216     for Index in range(0, len(SaltBridgesChainResiduesWords), 2):
 2217         Type = SaltBridgesChainResiduesWords[Index].strip()
 2218         Residue = SaltBridgesChainResiduesWords[Index + 1].strip()
 2219 
 2220         Residue = re.sub("[ ]+", " ", Residue)
 2221         ResidueWords = Residue.split(" ")
 2222 
 2223         CanonicalType = Type.lower()
 2224         if CanonicalType not in CanonicalSaltBridgesChainResiduesMap:
 2225             MiscUtil.PrintError(
 2226                 'The salt bridges chain residue type, %s, specified using "%s" option  is not a valid type: Supported salt bridges chain residue types: %s'
 2227                 % (Type, SaltBridgesOptionName, ", ".join(ValidSaltBridgesChainResidueTypes))
 2228             )
 2229         Type = CanonicalSaltBridgesChainResiduesMap[CanonicalType]
 2230 
 2231         if not Residue or len(ResidueWords) == 0:
 2232             MiscUtil.PrintError(
 2233                 'No residue specified for salt bridges chain residue type, %s, using "%s" option.'
 2234                 % (Type, SaltBridgesOptionName)
 2235             )
 2236 
 2237         SaltBridgesChainResiduesInfo[Type] = ResidueWords
 2238 
 2239     return SaltBridgesChainResiduesInfo
 2240 
 2241 
 2242 def ProcessChainSelectionsOptionsInfo(SelectionOptionName, SelectionOptionValue):
 2243     """Process names and selections specified  using command line option. It
 2244     is a pairwise list comma delimited values corresponding to PyMOL object
 2245     names and selection specification
 2246 
 2247     Arguments:
 2248         SelectionOptionName (str): Name of command line  option.
 2249         SelectionOptionValue (str): Value for command line option.
 2250 
 2251     Returns:
 2252         dict: A dictionary containing lists f names and selection commands.
 2253 
 2254     Examples:
 2255 
 2256         ChainSelectionsInfo =
 2257             PyMOLUtil.ProcessChainSelectionsOptionsInfo("--selectionsChain",
 2258             OptionsInfo["SelectionChains"])
 2259 
 2260     """
 2261 
 2262     # Initialize...
 2263     ChainSelectionsInfo = {}
 2264     ChainSelectionsInfo["Names"] = []
 2265     ChainSelectionsInfo["Selections"] = []
 2266 
 2267     if re.match("^None$", SelectionOptionValue, re.I):
 2268         return ChainSelectionsInfo
 2269 
 2270     # Parse selection chains names and selections...
 2271     ChainSelectionsWords = SelectionOptionValue.split(",")
 2272     if len(ChainSelectionsWords) % 2:
 2273         MiscUtil.PrintError(
 2274             'The number of comma delimited selection chains names and selections, %d, specified using "%s" option must be a multple of 2.'
 2275             % (len(ChainSelectionsWords), SelectionOptionName)
 2276         )
 2277 
 2278     CanonicalNamesMap = {}
 2279     for Index in range(0, len(ChainSelectionsWords), 2):
 2280         Name = ChainSelectionsWords[Index].strip()
 2281         Selection = ChainSelectionsWords[Index + 1].strip()
 2282 
 2283         if not len(Name):
 2284             MiscUtil.PrintError(
 2285                 'A name specified, "%s",  using "%s" option is empty.' % (SelectionOptionValue, SelectionOptionName)
 2286             )
 2287         if not len(Selection):
 2288             MiscUtil.PrintError(
 2289                 'A selection specified, "%s", using "%s" option is empty.' % (SelectionOptionValue, SelectionOptionName)
 2290             )
 2291 
 2292         CanonicalName = Name.lower()
 2293         if CanonicalName in CanonicalNamesMap:
 2294             MiscUtil.PrintError(
 2295                 'The name %s  specified using "%s" option is a duplicate name.' % (Name, SelectionOptionName)
 2296             )
 2297         CanonicalNamesMap[CanonicalName] = Name
 2298 
 2299         if re.search(r"[^a-zA-Z0-9 _\-]", Name, re.I):
 2300             MiscUtil.PrintError(
 2301                 'The name %s  specified using "%s" option contains invalid charactors. Supportted characters: alphanumeric,  space, hyphen and underscore..'
 2302             )
 2303 
 2304         ChainSelectionsInfo["Names"].append(Name)
 2305         ChainSelectionsInfo["Selections"].append(Selection)
 2306 
 2307     return ChainSelectionsInfo
 2308 
 2309 
 2310 def CalculateCenterOfMass(Selection="all", Quiet=0):
 2311     """Calculate center of mass for a selection.
 2312 
 2313     Arguments:
 2314         Selection (str): A PyMOL selection.
 2315         Quiet (int): Print information.
 2316 
 2317     Returns:
 2318         list: X, Y, Z coordinates for center of mass.
 2319 
 2320     """
 2321     MassTotal = 0.0
 2322     X, Y, Z = [0.0, 0.0, 0.0]
 2323 
 2324     Atoms = cmd.get_model(Selection)
 2325     for Atom in Atoms.atom:
 2326         Mass = Atom.get_mass()
 2327         MassTotal += Mass
 2328 
 2329         X += Atom.coord[0] * Mass
 2330         Y += Atom.coord[1] * Mass
 2331         Z += Atom.coord[2] * Mass
 2332 
 2333     XCOM = X / MassTotal
 2334     YCOM = Y / MassTotal
 2335     ZCOM = Z / MassTotal
 2336 
 2337     if not Quiet:
 2338         MiscUtil.PrintInfo("PyMOLUtil.CalculateCenterOfMass: %f, %f, %f" % (XCOM, YCOM, ZCOM))
 2339 
 2340     return [XCOM, YCOM, ZCOM]
 2341 
 2342 
 2343 def ConvertFileFormat(Infile, Outfile, Reinitialize=True, OutputFeedback=True):
 2344     """Convert infile to outfile by automatically detecting their formats
 2345     from the file extensions.
 2346 
 2347     The formats of both input and output files must be a valid format supported
 2348     by PyMOL.
 2349 
 2350     Arguments:
 2351         Infile (str): Name of input file.
 2352         Outfile (str): Name of outfile file.
 2353         Reinitialize (bool): Reinitialize PyMOL before loading input file.
 2354         OutputFeedback (bool): Control output feedback.
 2355 
 2356     """
 2357 
 2358     if not os.path.exists(Infile):
 2359         MiscUtil.PrintWarning("The input file, %s, doesn't exists.%s..." % (Infile))
 2360 
 2361     if Reinitialize:
 2362         cmd.reinitialize()
 2363 
 2364     if not OutputFeedback:
 2365         # Turn off output feedback...
 2366         MiscUtil.PrintInfo("Disabling output feedback for PyMOL...")
 2367         cmd.feedback("disable", "all", "output")
 2368 
 2369     FileDir, FileName, FileExt = MiscUtil.ParseFileName(Infile)
 2370     MolName = FileName
 2371 
 2372     cmd.load(Infile, MolName)
 2373     cmd.save(Outfile, MolName)
 2374     cmd.delete(MolName)
 2375 
 2376     if not OutputFeedback:
 2377         # Turn it back on...
 2378         MiscUtil.PrintInfo("\nEnabling output feedback for PyMOL...")
 2379         cmd.feedback("enable", "all", "output")
 2380 
 2381 
 2382 def ConvertPMLFileToPSEFile(PMLFile, PSEFile, Reinitialize=True, OutputFeedback=True):
 2383     """Convert PML file to PME file.
 2384 
 2385     Arguments:
 2386         PMLFile (str): Name of PML file.
 2387         PSEFile (str): Name of PSE file.
 2388         Reinitialize (bool): Reinitialize PyMOL before loading PML file.
 2389         OutputFeedback (bool): Control output feedback.
 2390 
 2391     """
 2392 
 2393     if not os.path.exists(PMLFile):
 2394         MiscUtil.PrintWarning("The PML file, %s, doesn't exists.%s..." % (PMLFile))
 2395 
 2396     if Reinitialize:
 2397         cmd.reinitialize()
 2398 
 2399     if not OutputFeedback:
 2400         # Turn off output feedback...
 2401         MiscUtil.PrintInfo("Disabling output feedback for PyMOL...")
 2402         cmd.feedback("disable", "all", "output")
 2403 
 2404     cmd.do("@%s" % PMLFile)
 2405     cmd.save(PSEFile)
 2406 
 2407     if not OutputFeedback:
 2408         # Turn it back on...
 2409         MiscUtil.PrintInfo("\nEnabling output feedback for PyMOL...")
 2410         cmd.feedback("enable", "all", "output")
 2411 
 2412 
 2413 def SetupPMLHeaderInfo(ScriptName=None, IncludeLocalPython=True):
 2414     """Setup header information for generating PML files. The local Python
 2415     functions are optionally embedded in the header information for their
 2416     use in PML files.
 2417 
 2418     Arguments:
 2419         ScriptName (str): Name of script calling the function.
 2420         IncludeLocalPython (bool): Include local Python functions.
 2421 
 2422     Returns:
 2423         str: Text containing header information for generating PML files.
 2424 
 2425     """
 2426     if ScriptName is None:
 2427         HeaderInfo = """\
 2428 #
 2429 # This file is automatically generated by a script available in MayaChemTools.
 2430 #
 2431 cmd.reinitialize()"""
 2432     else:
 2433         HeaderInfo = """\
 2434 #
 2435 # This file is automatically generated by the following PyMOL script available in
 2436 # MayaChemTools: %s
 2437 #
 2438 cmd.reinitialize() """ % (ScriptName)
 2439 
 2440     if IncludeLocalPython:
 2441         PMLForLocalPython = _SetupPMLForLocalPython()
 2442         HeaderInfo = "%s\n\n%s" % (HeaderInfo, PMLForLocalPython)
 2443 
 2444     return HeaderInfo
 2445 
 2446 
 2447 def _SetupPMLForLocalPython():
 2448     """Setup local Python functions for PML file."""
 2449 
 2450     PMLForPython = """\
 2451 ""
 2452 "Setting up local Python functions  for PML script..."
 2453 ""
 2454 python
 2455 
 2456 from __future__ import print_function
 2457 import re
 2458 
 2459 def ColorByHydrophobicity(Selection, ColorPalette = "RedToWhite"):
 2460     \"""Color by hydrophobicity using hydrophobic values for amino acid
 2461     residues corresponding to the Eisenberg hydrophobicity scale.
 2462     
 2463     Possible values for ColorPalette: RedToWhite or WhiteToGreen from most
 2464     hydrophobic amino acid to least hydrophobic.
 2465     
 2466     The colors values for amino acids are taken from color_h script avaiable
 2467     as part of the Script Library at PyMOL Wiki. 
 2468         
 2469     \"""
 2470         
 2471     if not re.match("^(RedToWhite|WhiteToGreen)$", ColorPalette, re.I):
 2472         print("Invalid ColorPalette value: %s. Valid values: RedToWhite, WhiteToGreen" % ColorPalette)
 2473     
 2474     ResColors = {}
 2475     ColorType = ""
 2476     
 2477     if re.match("^WhiteToGreen$", ColorPalette, re.I):
 2478         ColorType = "H2"
 2479         ResColors = {"ile" : [0.938,1,0.938], "phe" : [0.891,1,0.891], "val" : [0.844,1,0.844], "leu" : [0.793,1,0.793], "trp" : [0.746,1,0.746], "met" : [0.699,1,0.699], "ala" : [0.652,1,0.652], "gly" : [0.606,1,0.606], "cys" : [0.555,1,0.555], "tyr" : [0.508,1,0.508], "pro" : [0.461,1,0.461], "thr" : [0.414,1,0.414], "ser" : [0.363,1,0.363], "his" : [0.316,1,0.316], "glu" : [0.27,1,0.27], "asn" : [0.223,1,0.223], "gln" : [0.176,1,0.176], "asp" : [0.125,1,0.125], "lys" : [0.078,1,0.078], "arg" : [0.031,1,0.031]}
 2480     else:
 2481         ColorType = "H1"
 2482         ResColors = {"ile" : [0.996,0.062,0.062], "phe" : [0.996,0.109,0.109], "val" : [0.992,0.156,0.156], "leu" : [0.992,0.207,0.207], "trp" : [0.992,0.254,0.254], "met" : [0.988,0.301,0.301], "ala" : [0.988,0.348,0.348], "gly" : [0.984,0.394,0.394], "cys" : [0.984,0.445,0.445], "tyr" : [0.984,0.492,0.492], "pro" : [0.980,0.539,0.539], "thr" : [0.980,0.586,0.586], "ser" : [0.980,0.637,0.637], "his" : [0.977,0.684,0.684], "glu" : [0.977,0.730,0.730], "asn" : [0.973,0.777,0.777], "gln" : [0.973,0.824,0.824], "asp" : [0.973,0.875,0.875], "lys" : [0.899,0.922,0.922], "arg" : [0.899,0.969,0.969]}
 2483         
 2484     # Set up colors...
 2485     for ResName in ResColors:
 2486         ColorName = "color_%s_%s" % (ResName, ColorType)
 2487         cmd.set_color(ColorName, ResColors[ResName])
 2488         
 2489         ResSelection = "(%s and resn %s*)" % (Selection, ResName)
 2490         cmd.color(ColorName, ResSelection)
 2491     
 2492 cmd.extend("ColorByHydrophobicity", ColorByHydrophobicity)
 2493 
 2494 def ColorAtomsByHydrophobicityAndCharge(Selection, HydrophobicAtomsColor = "yellow", NegativelyChargedAtomsColor = "red", PositivelyChargedAtomsColor = "blue", OtherAtomsColor = "gray90"):
 2495     \"""Color atoms in amino acids by their propensity to make hydrophobic and
 2496     charge interactions [REF 140].  The atom names in standard amino acid
 2497     residues are used to identify atom types as shown below:
 2498     
 2499         Hydrophobic: C atoms not bound to N or O atoms
 2500         NegativelyCharged: Side chain O atoms in ASP and GLU
 2501         PositivelyCharged: Side chain N atoms in ARG and LYS
 2502         Others: Remaining atoms in polar and other residues
 2503          
 2504     The following color scheme is used by default:
 2505 
 2506         Hydrophobic: yellow
 2507         NegativelyCharged: red
 2508         PositivelyCharged: blue
 2509         Others: gray90
 2510     
 2511     The  amino acid atom names to color specific atoms  are taken from YRB.py
 2512     script [ REF 140]. The color values may also be specified as comma delimited
 2513     RGB triplets. For example: HydrophobicAtomsColor = "0.950 0.78 0.0",
 2514     NegativelyChargedAtomsColor = "1.0 0.4 0.4", PositivelyChargedAtomsColor
 2515     = "0.2 0.5 0.8", OtherAtomsColor = "0.95 0.95 0.95"
 2516     
 2517     \"""
 2518 
 2519     # Process colors...
 2520     Colors = {"HydrophobicAtomsColor": HydrophobicAtomsColor, "NegativelyChargedAtomsColor": NegativelyChargedAtomsColor, "PositivelyChargedAtomsColor": PositivelyChargedAtomsColor, "OtherAtomsColor": OtherAtomsColor}
 2521     ColorsRGB = {}
 2522     for ColorKey, ColorValue in Colors.items():
 2523         if re.search(" ", ColorValue):
 2524             ColorRGB = ColorValue.split(" ")
 2525         else:
 2526             ColorRGB = cmd.get_color_tuple(cmd.get_color_index(ColorValue))
 2527         ColorsRGB[ColorKey] = ColorRGB
 2528     
 2529     # Set up colors...
 2530     for ColorKey, ColorValue in ColorsRGB.items():
 2531         cmd.set_color(ColorKey, ColorValue)
 2532 
 2533     #  Set up colors for atom names across all resiudes...
 2534     AtomColors = {"OtherAtomsColor": "N,C,CA,O", "HydrophobicAtomsColor": "CB"}
 2535     
 2536     #  Set up colors for atom names across specific resiudes...
 2537     ResAtomColors = {"arg" : {"HydrophobicAtomsColor": "CG", "PositivelyChargedAtomsColor": "NE,NH2,NH1", "OtherAtomsColor": "CD,CZ"}, "asn" : {"OtherAtomsColor": "CG,OD1,ND2"}, "asp" : {"NegativelyChargedAtomsColor": "OD2,OD1", "OtherAtomsColor": "CG"}, "cys" : {"OtherAtomsColor": "SG"}, "gln" : {"HydrophobicAtomsColor": "CG", "OtherAtomsColor": "CD,OE1,NE2"}, "glu" : {"HydrophobicAtomsColor": "CG", "NegativelyChargedAtomsColor": "OE1,OE2", "OtherAtomsColor": "CD"}, "his" : {"OtherAtomsColor": "CG,CD2,ND1,NE2,CE1"}, "ile" : {"HydrophobicAtomsColor": "CG1,CG2,CD1"}, "leu" : {"HydrophobicAtomsColor": "CG,CD1,CD2"}, "lys" : {"HydrophobicAtomsColor": "CG,CD", "PositivelyChargedAtomsColor": "NZ", "OtherAtomsColor": "CE"}, "met" : {"HydrophobicAtomsColor": "CG,CE", "OtherAtomsColor": "SD"}, "phe" : {"HydrophobicAtomsColor": "CG,CD1,CE1,CZ,CE2,CD2"}, "pro" : {"HydrophobicAtomsColor": "CG", "OtherAtomsColor": "CD"}, "ser" : {"OtherAtomsColor": "CB,OG"}, "thr" : {"HydrophobicAtomsColor": "CG2", "OtherAtomsColor": "CB,OG1"}, "trp" : {"HydrophobicAtomsColor": "CG,CD2,CZ2,CH2,CZ3,CE3", "OtherAtomsColor": "CD1,NE1,CE2"}, "tyr" : {"HydrophobicAtomsColor": "CG,CE1,CD1,CE2,CD2", "OtherAtomsColor": "CZ,OH"}, "val" : {"HydrophobicAtomsColor": "CG1,CG2"}}
 2538     
 2539     #  Color atom names across all resiudes...
 2540     for ColorName in AtomColors:
 2541         AtomNames = AtomColors[ColorName]
 2542         AtomsSelection = "(%s and name %s)" % (Selection, AtomNames)
 2543         cmd.color(ColorName, AtomsSelection)
 2544     
 2545     #  Color hydrogen atoms across all residues to other color...
 2546     AtomsSelection = "(%s and hydro)" % (Selection)
 2547     cmd.color("OtherAtomsColor", AtomsSelection)
 2548     
 2549     #  Color atom names across specific resiudes...
 2550     for ResName in ResAtomColors:
 2551         for ColorName in ResAtomColors[ResName]:
 2552             AtomNames = ResAtomColors[ResName][ColorName]
 2553             AtomsSelection = "(%s and resn %s and name %s)" % (Selection, ResName, AtomNames)
 2554             cmd.color(ColorName, AtomsSelection)
 2555     
 2556 cmd.extend("ColorAtomsByHydrophobicityAndCharge", ColorAtomsByHydrophobicityAndCharge)
 2557 
 2558 def CheckAndDeleteEmptyObjects(ObjectNames, ParentObjectName = None):
 2559     \"""Delete an empty objects along with optionally deleting their parent.
 2560         
 2561     \"""
 2562     
 2563     ObjectNamesList = ObjectNames.split(",")
 2564 
 2565     AllObjectsEmpty = True
 2566     for ObjectName in ObjectNamesList:
 2567         ObjectName = ObjectName.strip()
 2568         if  cmd.count_atoms("(%s)" % ObjectName):
 2569             AllObjectsEmpty = False
 2570         else:
 2571             cmd.delete("%s" % ObjectName)
 2572     
 2573     if AllObjectsEmpty and ParentObjectName is not None:
 2574         cmd.delete("%s" % ParentObjectName)
 2575     
 2576 cmd.extend("CheckAndDeleteEmptyObjects", CheckAndDeleteEmptyObjects)
 2577 
 2578 python end"""
 2579 
 2580     return PMLForPython
 2581 
 2582 
 2583 def SetupPMLForEnableDisable(Name, Enable=True):
 2584     """Setup PML command for enabling or disabling display of a PyMOL object.
 2585 
 2586     Arguments:
 2587         Name (str): Name of a PyMOL object.
 2588         Enable (bool): Display status.
 2589 
 2590     Returns:
 2591         str: PML command for enabling or disabling display of an object.
 2592 
 2593     """
 2594 
 2595     if Enable:
 2596         PML = """cmd.enable("%s")""" % Name
 2597     else:
 2598         PML = """cmd.disable("%s")""" % Name
 2599 
 2600     return PML
 2601 
 2602 
 2603 def SetupPMLForGroup(GroupName, GroupMembersList, Enable=None, Action=None):
 2604     """Setup PML commands for creating a group from a list of group members. The
 2605     display and open status of the group may be optionally set. The 'None' values
 2606     for Enable and Action imply usage of PyMOL defaults for the creation of group.
 2607 
 2608     Arguments:
 2609         GroupName (str): Name of a PyMOL group.
 2610         GroupMembersList (list): List of group member names.
 2611         Enable (bool): Display status of group.
 2612         Action (str): Open or close status of group object.
 2613 
 2614     Returns:
 2615         str: PML commands for creating a group object.
 2616 
 2617     """
 2618 
 2619     PMLCmds = []
 2620 
 2621     GroupMembers = " ".join(GroupMembersList)
 2622     PMLCmds.append("""cmd.group("%s", "%s")""" % (GroupName, GroupMembers))
 2623 
 2624     if Enable is not None:
 2625         if Enable:
 2626             PMLCmds.append("""cmd.enable("%s")""" % GroupName)
 2627         else:
 2628             PMLCmds.append("""cmd.disable("%s")""" % GroupName)
 2629 
 2630     if Action is not None:
 2631         PMLCmds.append("""cmd.group("%s", action="%s")""" % (GroupName, Action))
 2632 
 2633     PML = "\n".join(PMLCmds)
 2634 
 2635     return PML
 2636 
 2637 
 2638 def SetupPMLForLigandView(Name, Selection, LigandResName, Enable=True, IgnoreHydrogens=False):
 2639     """Setup PML commands for creating a ligand view corresponding to a ligand
 2640     present in a selection. The ligand is identified using organic selection
 2641     operator available in PyMOL in conjunction with the specified ligand ID.
 2642     The ligand is colored by atom types and displayed as 'sticks'.
 2643 
 2644     Arguments:
 2645         Name (str): Name of a new PyMOL ligand object.
 2646         Selection (str): PyMOL selection containing ligand.
 2647         LigandResName (str): Ligand ID.
 2648         Enable (bool): Display status of ligand object.
 2649         IgnoreHydrogens (bool): Ignore hydrogens.
 2650 
 2651     Returns:
 2652         str: PML commands for a ligand view.
 2653 
 2654     """
 2655 
 2656     PMLCmds = []
 2657 
 2658     IgnoreHydrogensClause = " and (not hydro)" if IgnoreHydrogens else ""
 2659     PMLCmds.append(
 2660         """cmd.create("%s", "((%s) and organic and (resn %s)%s)")"""
 2661         % (Name, Selection, LigandResName, IgnoreHydrogensClause)
 2662     )
 2663 
 2664     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
 2665     PMLCmds.append("""util.cbag("%s", _self = cmd)""" % (Name))
 2666     PMLCmds.append("""cmd.show("sticks", "%s")""" % (Name))
 2667     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 2668 
 2669     PML = "\n".join(PMLCmds)
 2670 
 2671     return PML
 2672 
 2673 
 2674 def SetupPMLForLigandsInputFileView(Name, InputFile, Enable=True, IgnoreHydrogens=False):
 2675     """Setup PML commands for creating a ligand view corresponding to ligands
 2676     present in a SD file. The ligand is colored by atom types and displayed as 'sticks'.
 2677 
 2678     Arguments:
 2679         Name (str): Name of a new PyMOL ligand(s) object.
 2680         InputFile (str): Name of input file.
 2681         Enable (bool): Display status of ligand object.
 2682         IgnoreHydrogens (bool): Ignore hydrogens.
 2683 
 2684     Returns:
 2685         str: PML commands for a ligand view.
 2686 
 2687     """
 2688 
 2689     PMLCmds = []
 2690 
 2691     PMLCmds.append("""cmd.load("%s", "%s")""" % (InputFile, Name))
 2692 
 2693     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
 2694     PMLCmds.append("""util.cbag("%s", _self = cmd)""" % (Name))
 2695     PMLCmds.append("""cmd.show("sticks", "%s")""" % (Name))
 2696     if IgnoreHydrogens:
 2697         PMLCmds.append("""cmd.remove("(%s and hydro)")""" % (Name))
 2698 
 2699     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 2700 
 2701     PML = "\n".join(PMLCmds)
 2702 
 2703     return PML
 2704 
 2705 
 2706 def SetupPMLForLigandPocketView(Name, Selection, LigandSelection, DistanceCutoff, Enable=True, IgnoreHydrogens=False):
 2707     """Setup PML commands for creating a ligand binding pocket view
 2708     corresponding all residues present in a selection within a specified
 2709     distance from a ligand selection. The solvent and inorganic portions of
 2710     the selection are not included in the binding pocket. The pocket residues
 2711     are shown as 'lines'. The hydrogen atoms are not displayed.
 2712 
 2713     Arguments:
 2714         Name (str): Name of a new PyMOL binding pocket object.
 2715         Selection (str): PyMOL selection containing binding pocket residues.
 2716         LigandSelection (str): PyMOL selection containing ligand.
 2717         DistanceCutoff (float): Distance cutoff from ligand for selecting
 2718             binding pockect residues.
 2719         Enable (bool): Display status of binding pocket object.
 2720         IgnoreHydrogens (bool): Ignore hydrogens.
 2721 
 2722     Returns:
 2723         str: PML commands for a ligand binding pocket view.
 2724 
 2725     """
 2726 
 2727     PMLCmds = []
 2728 
 2729     IgnoreHydrogensClause = " and (not hydro)" if IgnoreHydrogens else ""
 2730     PMLCmds.append(
 2731         """cmd.create("%s", "((byresidue (%s) within %.1f of (%s)) and (not solvent) and (not inorganic) and (not organic)%s)")"""
 2732         % (Name, Selection, DistanceCutoff, LigandSelection, IgnoreHydrogensClause)
 2733     )
 2734 
 2735     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
 2736     PMLCmds.append("""cmd.show("lines", "(%s)")""" % (Name))
 2737     PMLCmds.append("""cmd.hide("(%s and hydro)")""" % (Name))
 2738     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 2739 
 2740     PML = "\n".join(PMLCmds)
 2741 
 2742     return PML
 2743 
 2744 
 2745 def SetupPMLForLigandPocketSolventView(Name, Selection, LigandSelection, DistanceCutoff, Enable=True):
 2746     """Setup PML commands for creating a ligand binding pocket view
 2747     corresponding to only solvent residues present in a selection within a
 2748     specified distance from a ligand selection. The solvent pocket residues
 2749     are shown as 'lines' and 'nonbonded'.
 2750 
 2751     Arguments:
 2752         Name (str): Name of a new PyMOL solvent binding pocket object.
 2753         Selection (str): PyMOL selection containing binding pocket residues.
 2754         LigandSelection (str): PyMOL selection containing ligand.
 2755         DistanceCutoff (float): Distance cutoff from ligand for selecting
 2756             binding pocket solvent residues.
 2757         Enable (bool): Display status of binding pocket object.
 2758 
 2759     Returns:
 2760         str: PML commands for a ligand binding pocket view only showing solvent
 2761             residues.
 2762 
 2763     """
 2764 
 2765     PMLCmds = []
 2766     PMLCmds.append(
 2767         """cmd.create("%s", "((byresidue (%s) within %.1f of (%s)) and solvent)")"""
 2768         % (Name, Selection, DistanceCutoff, LigandSelection)
 2769     )
 2770     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
 2771     PMLCmds.append("""cmd.show("nonbonded", "%s")""" % (Name))
 2772     PMLCmds.append("""cmd.show("lines", "%s")""" % (Name))
 2773     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 2774 
 2775     PML = "\n".join(PMLCmds)
 2776 
 2777     return PML
 2778 
 2779 
 2780 def SetupPMLForLigandPocketInorganicView(Name, Selection, LigandSelection, DistanceCutoff, Enable=True):
 2781     """Setup PML commands for creating a ligand binding pocket view
 2782     corresponding to only inorganic residues present in a selection within a
 2783     specified distance from a ligand selection. The inorganic pocket residues
 2784     are shown as 'lines' and 'nonbonded'.
 2785 
 2786     Arguments:
 2787         Name (str): Name of a new PyMOL solvent binding pocket object.
 2788         Selection (str): PyMOL selection containing binding pocket residues.
 2789         LigandSelection (str): PyMOL selection containing ligand.
 2790         DistanceCutoff (float): Distance cutoff from ligand for selecting
 2791             binding pocket inorganic residues.
 2792         Enable (bool): Display status of binding pocket object.
 2793 
 2794     Returns:
 2795         str: PML commands for a ligand binding pocket view only showing inorganic
 2796             residues.
 2797 
 2798     """
 2799 
 2800     PMLCmds = []
 2801     PMLCmds.append(
 2802         """cmd.create("%s", "((byresidue (%s) within %.1f of (%s)) and inorganic)")"""
 2803         % (Name, Selection, DistanceCutoff, LigandSelection)
 2804     )
 2805     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
 2806     PMLCmds.append("""cmd.show("nonbonded", "%s")""" % (Name))
 2807     PMLCmds.append("""cmd.show("lines", "%s")""" % (Name))
 2808     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 2809 
 2810     PML = "\n".join(PMLCmds)
 2811 
 2812     return PML
 2813 
 2814 
 2815 def SetupPMLForDistanceContactsView(
 2816     Name, Selection1, Selection2, Enable=True, Color="yellow", Cutoff=None, IgnoreHydrogens=True
 2817 ):
 2818     """Setup PML commands for creating distance contacts view between a pair of
 2819     selections. The distance contact view is generated using 'cmd.distance' command.
 2820     The distance labels are shown by default.
 2821 
 2822     Arguments:
 2823         Name (str): Name of a new PyMOL distance contacts object.
 2824         Selection1 (str): First PyMOL selection.
 2825         Selection2 (str): Second PyMOL selection.
 2826         Enable (bool): Display status of distance contacts object.
 2827         Color (str): Color for distance contact lines and labels.
 2828         DistanceCutoff (float): None or distance cutoff for distance contacts.
 2829         IgnoreHydrogens (bool): Ignore hydrogens for distance contacts.
 2830 
 2831     Returns:
 2832         str: PML commands for distance contacts view between a pair of selections.
 2833 
 2834     """
 2835 
 2836     if IgnoreHydrogens:
 2837         Selection1 = "(%s) and (not hydro)" % Selection1
 2838         Selection2 = "(%s) and (not hydro)" % Selection2
 2839 
 2840     PMLCmds = []
 2841     if Cutoff is None:
 2842         PMLCmds.append(
 2843             """cmd.distance("%s","(%s)","(%s)", quiet = 1, mode = 0, label = 1, reset = 1)"""
 2844             % (Name, Selection1, Selection2)
 2845         )
 2846     else:
 2847         PMLCmds.append(
 2848             """cmd.distance("%s","(%s)","(%s)", cutoff = %.1f, quiet = 1, mode = 0, label = 1, reset = 1)"""
 2849             % (Name, Selection1, Selection2, Cutoff)
 2850         )
 2851 
 2852     PMLCmds.append(SetupPMLForDeepColoring(Name, Color))
 2853     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 2854 
 2855     PML = "\n".join(PMLCmds)
 2856 
 2857     return PML
 2858 
 2859 
 2860 def SetupPMLForHalogenContactsView(
 2861     Name, Selection1, Selection2, Enable=True, Color="magenta", Cutoff=None, IgnoreHydrogens=True
 2862 ):
 2863     """Setup PML commands for creating halogen distance contacts view between a
 2864     pair of selections. The Selection1 corresponds to the selection containing halogens.
 2865     The halogen contact view is generated using 'cmd.distance' command. The
 2866     distance labels are shown by default.
 2867 
 2868     Arguments:
 2869         Name (str): Name of a new PyMOL halogen contacts object.
 2870         Selection1 (str): First PyMOL selection containing halogens.
 2871         Selection2 (str): Second PyMOL selection.
 2872         Enable (bool): Display status of halogen contacts object.
 2873         Color (str): Color for halogen contact lines and labels.
 2874         DistanceCutoff (float): None or distance cutoff for distance contacts.
 2875         IgnoreHydrogens (bool): Ignore hydrogens for halogen contacts.
 2876 
 2877     Returns:
 2878         str: PML commands for halogen contacts view between a pair of selections.
 2879 
 2880     """
 2881 
 2882     HalogenSelection1 = "(%s) and (elem F,Cl,Br,I)" % Selection1
 2883 
 2884     return SetupPMLForDistanceContactsView(Name, HalogenSelection1, Selection2, Enable, Color, Cutoff)
 2885 
 2886 
 2887 def SetupPMLForPiPiContactsView(Name, Selection1, Selection2, Enable=True, Color="yellow", Cutoff=None):
 2888     """Setup PML commands for creating pi pi contacts view between a pair of
 2889     selections. The pi pi contact view is generated using 'cmd.distance' command
 2890     with support for mode 6 and may require incentive version of PyMOL. The
 2891     distance labels are shown by default.
 2892 
 2893     Arguments:
 2894         Name (str): Name of a new PyMOL pi pi contacts object.
 2895         Selection1 (str): First PyMOL selection.
 2896         Selection2 (str): Second PyMOL selection.
 2897         Enable (bool): Display status of pi pi contacts object.
 2898         Color (str): Color for pi pi contact lines and labels.
 2899         DistanceCutoff (float): None or distance cutoff for pi pi contacts.
 2900 
 2901     Returns:
 2902         str: PML commands for pi pi contacts view between a pair of selections.
 2903 
 2904     """
 2905 
 2906     Mode = 6
 2907     return _SetupPMLForPiContactsView(Name, Selection1, Selection2, Mode, Enable, Color, Cutoff)
 2908 
 2909 
 2910 def SetupPMLForPiCationContactsView(Name, Selection1, Selection2, Enable=True, Color="yellow", Cutoff=None):
 2911     """Setup PML commands for creating pi cation contacts view between a pair of
 2912     selections. The pi pi contact view is generated using 'cmd.distance' command
 2913     with support for mode 7 and may require incentive version of PyMOL. The
 2914     distance labels are shown by default.
 2915 
 2916     Arguments:
 2917         Name (str): Name of a new PyMOL pi cation contacts object.
 2918         Selection1 (str): First PyMOL selection.
 2919         Selection2 (str): Second PyMOL selection.
 2920         Enable (bool): Display status of pi cation contacts object.
 2921         Color (str): Color for pi pi contact lines and labels.
 2922         DistanceCutoff (float): None or distance cutoff for pi cation contacts.
 2923 
 2924     Returns:
 2925         str: PML commands for pi pi contacts view between a pair of selections.
 2926 
 2927     """
 2928 
 2929     Mode = 7
 2930     return _SetupPMLForPiContactsView(Name, Selection1, Selection2, Mode, Enable, Color, Cutoff)
 2931 
 2932 
 2933 def _SetupPMLForPiContactsView(Name, Selection1, Selection2, Mode, Enable=True, Color="yellow", Cutoff=None):
 2934     """Setup PML commands for creating pi pi or pi cation contacts view between a
 2935     selections. The pi pi  and pi cation contact views are generated using 'cmd.distance'
 2936     command.
 2937     """
 2938 
 2939     PMLCmds = []
 2940     if Cutoff is None:
 2941         PMLCmds.append(
 2942             """cmd.distance("%s","(%s)","(%s)", quiet = 1, mode = %s, label = 1, reset = 1)"""
 2943             % (Name, Selection1, Selection2, Mode)
 2944         )
 2945     else:
 2946         PMLCmds.append(
 2947             """cmd.distance("%s","(%s)","(%s)", cutoff = %.1f, quiet = 1, mode = %s, label = 1, reset = 1)"""
 2948             % (Name, Selection1, Selection2, Cutoff, Mode)
 2949         )
 2950 
 2951     PMLCmds.append(SetupPMLForDeepColoring(Name, Color))
 2952     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 2953 
 2954     PML = "\n".join(PMLCmds)
 2955 
 2956     return PML
 2957 
 2958 
 2959 def SetupPMLForPolarContactsView(Name, Selection1, Selection2, Enable=True, Color="yellow", Cutoff=None):
 2960     """Setup PML commands for creating polar contacts view between a pair of
 2961     selections. The polar contact view is generated using 'cmd.dist' command. The
 2962     distance labels are shown by default.
 2963 
 2964     Arguments:
 2965         Name (str): Name of a new PyMOL polar contacts object.
 2966         Selection1 (str): First PyMOL selection.
 2967         Selection2 (str): Second PyMOL selection.
 2968         Enable (bool): Display status of polar contacts object.
 2969         Color (str): Color for polar contact lines and labels.
 2970         DistanceCutoff (float): None or distance cutoff for polar contacts.
 2971 
 2972     Returns:
 2973         str: PML commands for polar contacts view between a pair of selections.
 2974 
 2975     """
 2976 
 2977     PMLCmds = []
 2978     if Cutoff is None:
 2979         PMLCmds.append(
 2980             """cmd.distance("%s","(%s)","(%s)", quiet = 1, mode = 2, label = 1, reset = 1)"""
 2981             % (Name, Selection1, Selection2)
 2982         )
 2983     else:
 2984         PMLCmds.append(
 2985             """cmd.distance("%s","(%s)","(%s)", cutoff = %.1f, quiet = 1, mode = 2, label = 1, reset = 1)"""
 2986             % (Name, Selection1, Selection2, Cutoff)
 2987         )
 2988 
 2989     PMLCmds.append(SetupPMLForDeepColoring(Name, Color))
 2990     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 2991 
 2992     PML = "\n".join(PMLCmds)
 2993 
 2994     return PML
 2995 
 2996 
 2997 def SetupPMLForHydrophobicContactsView(Name, Selection1, Selection2, Enable=True, Color="yellow", Cutoff=None):
 2998     """Setup PML commands for creating hydrophobic contacts view between a pair of
 2999     selections. The hydrophobic contacts are shown between pairs of carbon atoms not
 3000     connected to hydrogen bond donor or acceptors atoms as identified by PyMOL. The
 3001     distance labels are shown by default.
 3002 
 3003     Arguments:
 3004         Name (str): Name of a new PyMOL polar contacts object.
 3005         Selection1 (str): First PyMOL selection.
 3006         Selection2 (str): Second PyMOL selection.
 3007         Enable (bool): Display status of polar contacts object.
 3008         Color (str): Color for polar contact lines and labels.
 3009         Cutoff (float): None or distance cutoff for hydrophobic contacts.
 3010 
 3011     Returns:
 3012         str: PML commands for polar contacts view between a pair of selections.
 3013 
 3014     """
 3015 
 3016     PMLCmds = []
 3017 
 3018     HydrophobicSelectionAtoms1 = "((%s) and (elem C) and (not bound_to (donors or acceptors)))" % (Selection1)
 3019     HydrophobicSelectionAtoms2 = "((%s) and (elem C) and (not bound_to (donors or acceptors)))" % (Selection2)
 3020 
 3021     if Cutoff is None:
 3022         PMLCmds.append(
 3023             """cmd.distance("%s","(%s)","(%s)", quiet = 1, mode = 0, label = 1, reset = 1)"""
 3024             % (Name, HydrophobicSelectionAtoms1, HydrophobicSelectionAtoms2)
 3025         )
 3026     else:
 3027         PMLCmds.append(
 3028             """cmd.distance("%s","(%s)","(%s)", cutoff = %.1f, quiet = 1, mode = 0,  label = 1, reset = 1)"""
 3029             % (Name, HydrophobicSelectionAtoms1, HydrophobicSelectionAtoms2, Cutoff)
 3030         )
 3031 
 3032     PMLCmds.append(SetupPMLForDeepColoring(Name, Color))
 3033     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 3034 
 3035     PML = "\n".join(PMLCmds)
 3036 
 3037     return PML
 3038 
 3039 
 3040 def SetupPMLForDeepColoring(Name, Color):
 3041     """Setup PML command for deep coloring based on PyMOL version number.
 3042 
 3043     Arguments:
 3044         Name (str): Name of a PyMOL object.
 3045         Color (str): Color for PyMOL object.
 3046 
 3047     Returns:
 3048         str: PML command for deep coloring.
 3049 
 3050     """
 3051 
 3052     if cmd.get_version()[1] < 2.0:
 3053         return """util.color_deep("%s", "%s")""" % (Color, Name)
 3054     else:
 3055         return """cmd.color_deep("%s", "%s")""" % (Color, Name)
 3056 
 3057 
 3058 def SetupPMLForAlignment(Method, RefSelection, FitSelection):
 3059     """Setup PML commands for aligning a pair of selection using  a specified
 3060     alignment method.
 3061 
 3062     Arguments:
 3063         Name (str): Name of a PyMOL object.
 3064 
 3065     Returns:
 3066         str: PML commands for aligning  a pair of selections.
 3067 
 3068     """
 3069 
 3070     PMLCmds = []
 3071     if re.match("^align$", Method, re.I):
 3072         PMLCmds.append("""cmd.align("(%s)", "(%s)")""" % (FitSelection, RefSelection))
 3073     elif re.match("^cealign$", Method, re.I):
 3074         PMLCmds.append("""cmd.cealign("(%s)", "(%s)")""" % (RefSelection, FitSelection))
 3075     elif re.match("^super$", Method, re.I):
 3076         PMLCmds.append("""cmd.super("(%s)", "(%s)")""" % (FitSelection, RefSelection))
 3077     else:
 3078         MiscUtil.PrintWarning("PyMOLUtil.SetupPMLForAlignment: Invalid method name: %s" % Method)
 3079 
 3080     PML = "\n".join(PMLCmds)
 3081 
 3082     return PML
 3083 
 3084 
 3085 def SetupPMLForBFactorCartoonView(Name, Selection, ColorPalette="blue_white_red", Enable=True):
 3086     """Setup PML commands for creating a B factor cartoon view for a specified
 3087     selection. The B factor values must be available for the atoms. The atoms
 3088     are colored using a color spectrum corresponding to a specified color
 3089     palette. Any valid PyMOL color palette name may be used.
 3090 
 3091     Arguments:
 3092         Name (str): Name of a new PyMOL B factor cartoon object.
 3093         Selection (str): Name of PyMOL selection.
 3094         ColorPalette (str): Name of color palette to use for color spectrum.
 3095         Enable (bool): Display status of B factor putty object.
 3096 
 3097     Returns:
 3098         str: PML commands for B factor putty view.
 3099 
 3100     """
 3101 
 3102     return _SetupPMLForBFactorView(Name, Selection, ColorPalette, Enable, Putty=False)
 3103 
 3104 
 3105 def SetupPMLForBFactorPuttyView(Name, Selection, ColorPalette="blue_white_red", Enable=True):
 3106     """Setup PML commands for creating a B factor putty view for a specified
 3107     selection. The B factor values must be available for the atoms. The atoms
 3108     are colored using a color spectrum corresponding to a specified color
 3109     palette. Any valid PyMOL color palette name may be used.
 3110 
 3111     Arguments:
 3112         Name (str): Name of a new PyMOL B factor putty object.
 3113         Selection (str): Name of PyMOL selection.
 3114         ColorPalette (str): Name of color palette to use for color spectrum.
 3115         Enable (bool): Display status of B factor putty object.
 3116 
 3117     Returns:
 3118         str: PML commands for B factor putty view.
 3119 
 3120     """
 3121 
 3122     return _SetupPMLForBFactorView(Name, Selection, ColorPalette, Enable, Putty=True)
 3123 
 3124 
 3125 def _SetupPMLForBFactorView(Name, Selection, ColorPalette="blue_white_red", Enable=True, Putty=True):
 3126     """Setup PML commands for creating a B factor cartoon or putty view for a
 3127     specified selection. The B factor values must be available for the atoms.
 3128     """
 3129 
 3130     PMLCmds = []
 3131     PMLCmds.append("""cmd.create("%s", "(%s)")""" % (Name, Selection))
 3132     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
 3133     PMLCmds.append("""cmd.spectrum("b", "%s", "(%s)")""" % (ColorPalette, Name))
 3134     PMLCmds.append("""cmd.show("cartoon", "%s")""" % (Name))
 3135     if Putty:
 3136         PMLCmds.append("""cmd.cartoon("putty", "%s")""" % (Name))
 3137     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 3138 
 3139     PML = "\n".join(PMLCmds)
 3140 
 3141     return PML
 3142 
 3143 
 3144 def SetupPMLForHydrophobicSurfaceView(Name, Selection, ColorPalette="RedToWhite", Enable=True, DisplayAs="cartoon"):
 3145     """Setup PML commands for creating a hydrophobic surface view for a specified
 3146     selection. The surfaces are colored using a specified color palette. This is only valid
 3147     for amino acids.
 3148 
 3149     Arguments:
 3150         Name (str): Name of a new PyMOL hydrophobic surface object.
 3151         Selection (str): Name of PyMOL selection.
 3152         ColorPalette (str): Name of color palette to use for coloring surfaces.
 3153             Possible values: RedToWhite or WhiteToGreen for most hydrophobic
 3154             to least hydrophobic amino acids.
 3155         Enable (bool): Display status of surface object.
 3156         DisplayAs (str): Any additional valid display type such as lines,
 3157             sticks, ribbon, cartoon, or None.
 3158 
 3159     Returns:
 3160         str: PML commands for hydrophobic surface view.
 3161 
 3162     """
 3163 
 3164     PMLCmds = _GetPMLCmdsForSurfaceView(Name, Selection, DisplayAs)
 3165     PMLCmds.append("""ColorByHydrophobicity("%s", "%s")""" % (Name, ColorPalette))
 3166     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 3167 
 3168     PML = "\n".join(PMLCmds)
 3169 
 3170     return PML
 3171 
 3172 
 3173 def SetupPMLForHydrophobicAndChargeSurfaceView(
 3174     Name,
 3175     Selection,
 3176     HydrophobicAtomsColor="yellow",
 3177     NegativelyChargedAtomsColor="red",
 3178     PositivelyChargedAtomsColor="blue",
 3179     OtherAtomsColor="gray90",
 3180     Enable=True,
 3181     DisplayAs="cartoon",
 3182 ):
 3183     """Setup PML commands for creating a surface colored by hydrophobic and
 3184     charge [ REF 140] properties of atoms in amino acids. The atom names in
 3185     standard amino acid residues are used to identify atom types as shown below:
 3186 
 3187     Hydrophobic: C atoms not bound to N or O atoms; NegativelyCharged: Side
 3188     chain O atoms in ASP and GLU; PositivelyCharged: Side chain N atoms in
 3189     ARG and LYS; Others: Remaining atoms in polar and other residues
 3190 
 3191     The  amino acid atom names to color specific atoms  are taken from YRB.py
 3192     script [ REF 140]. The color values may also be specified as comma delimited
 3193     RGB triplets. For example: HydrophobicAtomsColor = "0.950 0.78 0.0",
 3194     NegativelyChargedAtomsColor = "1.0 0.4 0.4", PositivelyChargedAtomsColor
 3195     = "0.2 0.5 0.8", OtherAtomsColor = "0.95 0.95 0.95"
 3196 
 3197     Arguments:
 3198         Name (str): Name of a new PyMOL hydrophobic surface object.
 3199         Selection (str): Name of PyMOL selection.
 3200         HydrophobicAtomsColor (str): Color name or space delimited RGB values
 3201         NegativelyChargedAtomsColor (str): Color name or space delimited RGB values
 3202         PositivelyChargedAtomsColor (str): Color name or space delimited RGB values
 3203         OtherAtomsColor (str): Color name or space delimited RGB values
 3204         Enable (bool): Display status of surface object.
 3205         DisplayAs (str): Any additional valid display type such as lines,
 3206             sticks, ribbon, cartoon, or None.
 3207 
 3208     Returns:
 3209         str: PML commands for hydrophobic and charge surface view.
 3210 
 3211     """
 3212 
 3213     PMLCmds = _GetPMLCmdsForSurfaceView(Name, Selection, DisplayAs)
 3214     PMLCmds.append(
 3215         """ColorAtomsByHydrophobicityAndCharge("%s", "%s", "%s", "%s", "%s")"""
 3216         % (Name, HydrophobicAtomsColor, NegativelyChargedAtomsColor, PositivelyChargedAtomsColor, OtherAtomsColor)
 3217     )
 3218     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 3219 
 3220     PML = "\n".join(PMLCmds)
 3221 
 3222     return PML
 3223 
 3224 
 3225 def SetupPMLForSurfaceView(Name, Selection, Enable=True, DisplayAs="cartoon", Color="None"):
 3226     """Setup PML commands for creating a molecular surface view for a specified
 3227     selection.
 3228 
 3229     Arguments:
 3230         Name (str): Name of a new PyMOL molecular surface object.
 3231         Selection (str): Name of PyMOL selection.
 3232         Enable (bool): Display status of surface object.
 3233         DisplayAs (str): Any additional valid display type such as lines,
 3234             sticks, ribbon, cartoon, or None.
 3235         Color (str): Surafce color.
 3236 
 3237     Returns:
 3238         str: PML commands for molecular surface view.
 3239 
 3240     """
 3241 
 3242     PMLCmds = _GetPMLCmdsForSurfaceView(Name, Selection, DisplayAs)
 3243     if Color is not None:
 3244         PMLCmds.append(SetupPMLForDeepColoring(Name, Color))
 3245     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 3246 
 3247     PML = "\n".join(PMLCmds)
 3248 
 3249     return PML
 3250 
 3251 
 3252 def _GetPMLCmdsForSurfaceView(Name, Selection, DisplayAs="cartoon"):
 3253     """Setup PML command for surface view."""
 3254 
 3255     PMLCmds = []
 3256     PMLCmds.append("""cmd.create("%s", "(%s)")""" % (Name, Selection))
 3257     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
 3258     if DisplayAs is not None:
 3259         PMLCmds.append("""cmd.show("%s", "%s")""" % (DisplayAs, Name))
 3260     PMLCmds.append("""cmd.show("surface", "%s")""" % (Name))
 3261 
 3262     return PMLCmds
 3263 
 3264 
 3265 def SetupPMLForSelectionDisplayView(Name, Selection, DisplayAs, Color=None, Enable=True, IgnoreHydrogens=False):
 3266     """Setup PML commands for creating a specific molecular display view for a
 3267      selection.
 3268 
 3269     Arguments:
 3270         Name (str): Name of a new PyMOL object.
 3271         Selection (str): Name of PyMOL selection.
 3272         DisplayAs (str): Any valid display type such as lines, sticks, ribbon,
 3273             cartoon, or surface
 3274         Color (str): Color name or use default color.
 3275         Enable (bool): Display status of object.
 3276         IgnoreHydrogens (bool): Ignore hydrogens.
 3277 
 3278     Returns:
 3279         str: PML commands for molecular selection view.
 3280 
 3281     """
 3282 
 3283     PMLCmds = []
 3284     if IgnoreHydrogens:
 3285         PMLCmds.append("""cmd.create("%s", "((%s) and (not hydro))")""" % (Name, Selection))
 3286     else:
 3287         PMLCmds.append("""cmd.create("%s", "(%s)")""" % (Name, Selection))
 3288     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
 3289     PMLCmds.append("""cmd.show("%s", "%s")""" % (DisplayAs, Name))
 3290     if Color is not None:
 3291         PMLCmds.append(SetupPMLForDeepColoring(Name, Color))
 3292     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 3293 
 3294     PML = "\n".join(PMLCmds)
 3295 
 3296     return PML
 3297 
 3298 
 3299 def SetupPMLForBallAndStickView(Name, Selection, Enable=True, SphereScale=0.3, StickRadius=0.2):
 3300     """Setup PML commands for creating a ball and stick view for a specified
 3301     selection.
 3302 
 3303     Arguments:
 3304         Name (str): Name of a new PyMOL ball and stick object.
 3305         Selection (str): Name of PyMOL selection.
 3306         Enable (bool): Display status of ball and stick object.
 3307         SphereScale (float): Scaling factor for sphere radii.
 3308         StickScale (float): Scaling factor for stick radii.
 3309 
 3310     Returns:
 3311         str: PML commands for ball and stick view.
 3312 
 3313     """
 3314 
 3315     PMLCmds = []
 3316     PMLCmds.append("""cmd.create("%s", "(%s)")""" % (Name, Selection))
 3317     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
 3318     PMLCmds.append("""cmd.show("sphere", "%s")""" % (Name))
 3319     PMLCmds.append("""cmd.show("sticks", "%s")""" % (Name))
 3320     PMLCmds.append("""cmd.set("sphere_scale", %.1f, "%s")""" % (SphereScale, Name))
 3321     PMLCmds.append("""cmd.set("stick_radius", %.1f, "%s")""" % (StickRadius, Name))
 3322 
 3323     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 3324 
 3325     PML = "\n".join(PMLCmds)
 3326 
 3327     return PML
 3328 
 3329 
 3330 def SetupPMLForInorganicView(Name, Selection, Enable=True):
 3331     """Setup PML commands for creating a inorganic view corresponding to
 3332     inorganic residues present in a selection. The inorganic residues are
 3333     identified using inorganic selection operator available in PyMOL. The
 3334     inorganic residues are displayed as 'lines' and 'nonbonded'.
 3335 
 3336     Arguments:
 3337         Name (str): Name of a new PyMOL inorganic object.
 3338         Selection (str): Name of PyMOL selection.
 3339         Enable (bool): Display status of inorganic object.
 3340 
 3341     Returns:
 3342         str: PML commands for inorganic view.
 3343 
 3344     """
 3345 
 3346     PMLCmds = []
 3347     PMLCmds.append("""cmd.create("%s", "((%s) and inorganic)")""" % (Name, Selection))
 3348     PMLCmds.append("""cmd.show("nonbonded", "%s")""" % (Name))
 3349     PMLCmds.append("""cmd.show("lines", "%s")""" % (Name))
 3350     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 3351 
 3352     PML = "\n".join(PMLCmds)
 3353 
 3354     return PML
 3355 
 3356 
 3357 def SetupPMLForSolventView(Name, Selection, Enable=True):
 3358     """Setup PML commands for creating a solvent view corresponding to
 3359     solvent residues present in a selection. The solvent residues are
 3360     identified using solvent selection operator available in PyMOL. The
 3361     solvent residues are displayed as 'nonbonded'.
 3362 
 3363     Arguments:
 3364         Name (str): Name of a new PyMOL solvent object.
 3365         Selection (str): Name of PyMOL selection.
 3366         Enable (bool): Display status of inorganic object.
 3367 
 3368     Returns:
 3369         str: PML commands for solvent view.
 3370 
 3371     """
 3372 
 3373     PMLCmds = []
 3374     PMLCmds.append("""cmd.create("%s", "((%s) and solvent)")""" % (Name, Selection))
 3375     PMLCmds.append("""cmd.show("nonbonded", "%s")""" % (Name))
 3376     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 3377 
 3378     PML = "\n".join(PMLCmds)
 3379 
 3380     return PML
 3381 
 3382 
 3383 def SetupPMLForDisulfideBondsView(Name, Selection, DisplayAs, Enable=True):
 3384     """Setup PML commands for creating a  view corresponding to
 3385     disulfide bonds present in a selection.
 3386 
 3387     Arguments:
 3388         Name (str): Name of a new PyMOL disulfide bonds object.
 3389         Selection (str): Name of PyMOL selection.
 3390         DisplayAs (str): Any valid display type such as lines, sticks, ribbon,
 3391             cartoon, or surface
 3392         Enable (bool): Display status of disulfide bonds object.
 3393 
 3394     Returns:
 3395         str: PML commands for disulfide bonds view.
 3396 
 3397     """
 3398 
 3399     PMLCmds = []
 3400 
 3401     DisulfideBondsSelection = (
 3402         "(byres (((%s) and (resn CYS+CYX) and (name SG)) and bound_to ((%s) and (resn CYS+CYX) and (name SG))))"
 3403         % (Selection, Selection)
 3404     )
 3405     PMLCmds.append("""cmd.create("%s", "(%s)")""" % (Name, DisulfideBondsSelection))
 3406     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
 3407     PMLCmds.append("""util.cbag("%s", _self = cmd)""" % (Name))
 3408     PMLCmds.append("""cmd.show("%s", "%s")""" % (DisplayAs, Name))
 3409     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 3410 
 3411     PML = "\n".join(PMLCmds)
 3412 
 3413     return PML
 3414 
 3415 
 3416 def SetupPMLForSaltBridgesResiduesView(Name, Selection, Residues, DisplayAs, Enable=True):
 3417     """Setup PML commands for creating a  view corresponding to residues
 3418     for salt bridges present in a selection.
 3419 
 3420     Arguments:
 3421         Name (str): Name of a new PyMOL disulfide bonds object.
 3422         Selection (str): Name of PyMOL selection.
 3423         Residues (list): List of residues.
 3424         DisplayAs (str): Any valid display type such as lines, sticks, ribbon,
 3425             cartoon, or surface
 3426         Enable (bool): Display status of salt bridges residues object.
 3427 
 3428     Returns:
 3429         str: PML commands for salt bridges residues view.
 3430 
 3431     """
 3432 
 3433     PMLCmds = []
 3434 
 3435     SaltBridgesResiduesSelection = "((%s) and (resn %s) and (not name N+O) and (not hydro))" % (
 3436         Selection,
 3437         "+".join(Residues),
 3438     )
 3439 
 3440     PMLCmds.append("""cmd.create("%s", "(%s)")""" % (Name, SaltBridgesResiduesSelection))
 3441     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
 3442     PMLCmds.append("""util.cbag("%s", _self = cmd)""" % (Name))
 3443     PMLCmds.append("""cmd.show("%s", "%s")""" % (DisplayAs, Name))
 3444     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 3445 
 3446     PML = "\n".join(PMLCmds)
 3447 
 3448     return PML
 3449 
 3450 
 3451 def SetupPMLForPolymerChainView(Name, Selection, Enable=True):
 3452     """Setup PML commands for creating a polymer chain view corresponding
 3453     to backbone and sidechain residues in a selection. The polymer chain is
 3454     displayed as 'cartoon'.
 3455 
 3456     Arguments:
 3457         Name (str): Name of a new PyMOL polymer chain object.
 3458         Selection (str): Name of PyMOL selection.
 3459         Enable (bool): Display status of chain object.
 3460 
 3461     Returns:
 3462         str: PML commands for polymer chain view.
 3463 
 3464     """
 3465 
 3466     PMLCmds = []
 3467     PMLCmds.append("""cmd.create("%s", "((%s) and (backbone or sidechain))")""" % (Name, Selection))
 3468     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
 3469     PMLCmds.append("""util.cbag("%s", _self = cmd)""" % (Name))
 3470     PMLCmds.append("""cmd.show("cartoon", "%s")""" % (Name))
 3471     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 3472 
 3473     PML = "\n".join(PMLCmds)
 3474 
 3475     return PML
 3476 
 3477 
 3478 def SetupPMLForPolymerComplexView(
 3479     MoleculeName, PDBFile, Enable=True, ShowSolvent=True, ShowInorganic=True, ShowLines=True
 3480 ):
 3481     """Setup PML commands for creating a polymer complex view for all chains
 3482     in a PDB file. The solvent and inorganic residues are also shown by default.
 3483      The polymer chains are displayed as 'cartoon'. The 'line' display for the
 3484     polymer chains is also shown and may be turned off. The organic residues are
 3485     displayed as 'sticks'. The solvent and inorganic residues are displayed as
 3486     'nonbonded' and 'lines'.
 3487 
 3488     Arguments:
 3489         MoleculeName (str): Name of a new PyMOL molecule object.
 3490         PDBFile (str): Name of PDB file.
 3491         Enable (bool): Display status of chain object.
 3492         ShowSolvent (bool): Display solvent residues.
 3493         ShowInorganic (bool): Display inorganic residues.
 3494         ShowLines (bool): Display lines for polymer chains.
 3495 
 3496     Returns:
 3497         str: PML commands for polymer complex view.
 3498 
 3499     """
 3500 
 3501     PMLCmds = []
 3502 
 3503     PMLCmds.append("""cmd.load("%s", "%s")""" % (PDBFile, MoleculeName))
 3504     PML = _SetupPMLForPolymerComplexView(MoleculeName, Enable, ShowSolvent, ShowInorganic, ShowLines)
 3505     PMLCmds.append(PML)
 3506 
 3507     PML = "\n".join(PMLCmds)
 3508 
 3509     return PML
 3510 
 3511 
 3512 def SetupPMLForPolymerChainComplexView(
 3513     ChainComplexName, Selection, ChainName, Enable=True, ShowSolvent=True, ShowInorganic=True, ShowLines=True
 3514 ):
 3515     """Setup PML commands for creating a polymer chain complex view for a specified
 3516     chain in a selection. The solvent and inorganic residues are also shown by
 3517     default. The polymer chain is displayed as 'cartoon'. The 'line' display for the
 3518     polymer chain is also shown and may be turned off. The organic residues are
 3519     displayed as 'sticks'. The solvent and inorganic residues are displayed as
 3520     'nonbonded' and 'lines'.
 3521 
 3522     Arguments:
 3523         ChainComplexName (str): Name of a new PyMOL polymer chain complex.
 3524         Selection (str): Name of PyMOL selection.
 3525         ChainName (str): Name of a chain.
 3526         Enable (bool): Display status of chain object.
 3527         ShowSolvent (bool): Display solvent residues.
 3528         ShowInorganic (bool): Display inorganic residues.
 3529         ShowLines (bool): Display lines for polymer chain.
 3530 
 3531     Returns:
 3532         str: PML commands for polymer chain complex view.
 3533 
 3534     """
 3535 
 3536     PMLCmds = []
 3537 
 3538     PMLCmds.append("""cmd.create("%s", "(%s and chain %s)")""" % (ChainComplexName, Selection, ChainName))
 3539     PML = _SetupPMLForPolymerComplexView(ChainComplexName, Enable, ShowSolvent, ShowInorganic, ShowLines)
 3540     PMLCmds.append(PML)
 3541 
 3542     PML = "\n".join(PMLCmds)
 3543 
 3544     return PML
 3545 
 3546 
 3547 def _SetupPMLForPolymerComplexView(Name, Enable=True, ShowSolvent=True, ShowInorganic=True, ShowLines=False):
 3548     """Setup PML for creating a polymer complex view."""
 3549 
 3550     PMLCmds = []
 3551 
 3552     PMLCmds.append("""cmd.hide("everything", "%s")""" % (Name))
 3553     PMLCmds.append("""cmd.show("cartoon", "%s")""" % (Name))
 3554     PMLCmds.append("""util.cba(33, "%s", _self = cmd)""" % (Name))
 3555     PMLCmds.append("""cmd.show("sticks", "(organic and (%s))")""" % (Name))
 3556     if ShowSolvent:
 3557         PMLCmds.append("""cmd.show("nonbonded", "(solvent and (%s))")""" % (Name))
 3558     if ShowInorganic:
 3559         PMLCmds.append("""cmd.show("nonbonded", "(inorganic and (%s))")""" % (Name))
 3560 
 3561     if ShowLines:
 3562         PMLCmds.append("""cmd.show("lines", "%s")""" % (Name))
 3563     else:
 3564         if ShowInorganic:
 3565             PMLCmds.append("""cmd.show("lines", "(inorganic and (%s))")""" % (Name))
 3566 
 3567     PMLCmds.append("""cmd.set_bond("valence", "1", "%s", quiet = 1)""" % (Name))
 3568     PMLCmds.append(SetupPMLForEnableDisable(Name, Enable))
 3569 
 3570     PML = "\n".join(PMLCmds)
 3571 
 3572     return PML