MayaChemTools

    1 #!/bin/env python
    2 #
    3 # File: PyMOLInfoMacromolecules.py
    4 # Author: Manish Sud <msud@san.rr.com>
    5 #
    6 # Copyright (C) 2019 Manish Sud. All rights reserved.
    7 #
    8 # The functionality available in this script is implemented using PyMOL, a
    9 # molecular visualization system on an open source foundation originally
   10 # developed by Warren DeLano.
   11 #
   12 # This file is part of MayaChemTools.
   13 #
   14 # MayaChemTools is free software; you can redistribute it and/or modify it under
   15 # the terms of the GNU Lesser General Public License as published by the Free
   16 # Software Foundation; either version 3 of the License, or (at your option) any
   17 # later version.
   18 #
   19 # MayaChemTools is distributed in the hope that it will be useful, but without
   20 # any warranty; without even the implied warranty of merchantability of fitness
   21 # for a particular purpose.  See the GNU Lesser General Public License for more
   22 # details.
   23 #
   24 # You should have received a copy of the GNU Lesser General Public License
   25 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
   26 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
   27 # Boston, MA, 02111-1307, USA.
   28 #
   29 
   30 from __future__ import print_function
   31 
   32 # Add local python path to the global path and import standard library modules...
   33 import os
   34 import sys;  sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), "..", "lib", "Python"))
   35 import time
   36 import re
   37 
   38 # PyMOL imports...
   39 try:
   40     import pymol
   41     # Finish launching PyMOL in  a command line mode for batch processing (-c)
   42     # along with the following options:  disable loading of pymolrc and plugins (-k);
   43     # suppress start up messages (-q)
   44     pymol.finish_launching(['pymol', '-ckq'])
   45 except ImportError as ErrMsg:
   46     sys.stderr.write("\nFailed to import PyMOL module/package: %s\n" % ErrMsg)
   47     sys.stderr.write("Check/update your PyMOL environment and try again.\n\n")
   48     sys.exit(1)
   49 
   50 # MayaChemTools imports...
   51 try:
   52     from docopt import docopt
   53     import MiscUtil
   54     import PyMOLUtil
   55 except ImportError as ErrMsg:
   56     sys.stderr.write("\nFailed to import MayaChemTools module/package: %s\n" % ErrMsg)
   57     sys.stderr.write("Check/update your MayaChemTools environment and try again.\n\n")
   58     sys.exit(1)
   59 
   60 ScriptName = os.path.basename(sys.argv[0])
   61 Options = {}
   62 OptionsInfo = {}
   63 
   64 def main():
   65     """Start execution of the script"""
   66     
   67     MiscUtil.PrintInfo("\n%s (PyMOL v%s; %s) Starting...\n" % (ScriptName, pymol.cmd.get_version()[0], time.asctime()))
   68     
   69     (WallClockTime, ProcessorTime) = MiscUtil.GetWallClockAndProcessorTime()
   70     
   71     # Retrieve command line arguments and options...
   72     RetrieveOptions()
   73     
   74     # Process and validate command line arguments and options...
   75     ProcessOptions()
   76 
   77     # Perform actions required by the script...
   78     ListInfo()
   79     
   80     MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName)
   81     MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime))
   82 
   83 def ListInfo():
   84     """List information for macromolecules."""
   85 
   86     for Infile in OptionsInfo["InfilesNames"]:
   87         MiscUtil.PrintInfo("\nProcessing file %s..." % Infile)
   88         ListFileInfo(Infile)
   89 
   90 def ListFileInfo(Infile):
   91     """List information for macromolecules in a file."""
   92     
   93     FileDir, FileName, FileExt = MiscUtil.ParseFileName(Infile)
   94     MolName = FileName
   95 
   96     # Load infile...
   97     pymol.cmd.load(Infile, MolName)
   98     
   99     ChainIDs = PyMOLUtil.GetChains(MolName)
  100     ListHeaderInfo(Infile)
  101     
  102     ListChainsInfo(MolName, ChainIDs)
  103     ListChainsResiduesInfo(MolName, ChainIDs)
  104     
  105     ListLigandsInfo(MolName, ChainIDs)
  106     ListSolventsInfo(MolName, ChainIDs)
  107     ListInorganicsInfo(MolName, ChainIDs)
  108 
  109     ListPocketsInfo(MolName, ChainIDs)
  110     
  111     ListInterfaceResiduesInfo(MolName, ChainIDs)
  112     ListSurfaceResiduesInfo(MolName, ChainIDs)
  113     ListPhiPsiAnglesInfo(MolName, ChainIDs)
  114     
  115     ListBoundingBoxInfo(MolName)
  116     
  117     # Delete infile object...
  118     pymol.cmd.delete(MolName)
  119     
  120     ListFileSizeAndModificationInfo(Infile)
  121 
  122 def ListHeaderInfo(Infile):
  123     """List header information."""
  124 
  125     if not (OptionsInfo["All"] or OptionsInfo["Header"]):
  126         return
  127 
  128     FileDir, FileName, FileExt = MiscUtil.ParseFileName(Infile)
  129 
  130     Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution = ["Not Available"] * 5
  131     if re.match("^pdb$", FileExt, re.I):
  132          Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution = RetriveHeadAndExperimentalInfoFromPDBFile(Infile)
  133     elif re.match("^cif$", FileExt, re.I):
  134          Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution = RetriveHeadAndExperimentalInfoFromCIFFile(Infile)
  135 
  136     MiscUtil.PrintInfo("\nID: %s\nClassification: %s\nDeposition date: %s" % (IDCode, Classification, DepositionDate))
  137     MiscUtil.PrintInfo("\nExperimental technique: %s\nResolution: %s" % (ExperimentalTechnique, Resolution))
  138 
  139 def RetriveHeadAndExperimentalInfoFromPDBFile(Infile):
  140     """Retrieve header and experimental information from PDB file. """
  141 
  142     Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution = ["Not Available"] * 5
  143 
  144     Lines = MiscUtil.GetTextLines(Infile)
  145 
  146     # Retrieve header info...
  147     for Line in Lines:
  148         if re.match("^HEADER", Line, re.I):
  149             # Format: 10x40s9s3x4s
  150             FormatSize = 66
  151             Line = PrepareLineForFormatSize(Line, FormatSize)
  152             
  153             Classification = Line[10:50]
  154             DepositionDate = Line[50:59]
  155             IDCode = Line[62:66]
  156             
  157             Classification = Classification.strip() if len(Classification.strip()) else "Not Available"
  158             DepositionDate = DepositionDate.strip() if len(DepositionDate.strip()) else "Not Available"
  159             IDCode = IDCode.strip() if len(IDCode.strip()) else "Not Available"
  160             
  161             break
  162 
  163     # Retrieve experimental info...
  164     for Line in Lines:
  165         if re.match("^EXPDTA", Line, re.I):
  166             ExperimentalTechnique = re.sub("^EXPDTA", "", Line, re.I)
  167             ExperimentalTechnique = ExperimentalTechnique.strip()
  168         elif re.match("^REMARK   2 RESOLUTION.", Line, re.I):
  169             if re.search("NOT APPLICABLE", Line, re.I):
  170                 Resolution = "NOT APPLICABLE"
  171             else:
  172                 FormatSize = 70
  173                 Line = PrepareLineForFormatSize(Line, FormatSize)
  174                 Resolution = Line[22:70]
  175                 Resolution = Resolution.strip() if len(Resolution.strip()) else "Not Available"
  176         elif re.match("^(ATOM|HETATM)", Line, re.I):
  177             break
  178 
  179     return Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution
  180 
  181 def RetriveHeadAndExperimentalInfoFromCIFFile(Infile):
  182     """Retrieve header information from CIF file. """
  183 
  184     Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution = ["Not Available"] * 5
  185     
  186     Lines = MiscUtil.GetTextLines(Infile)
  187     
  188     # IDCode...
  189     for Line in Lines:
  190         if re.match("^_struct_keywords.entry_id", Line, re.I):
  191             IDCode = re.sub("^_struct_keywords.entry_id", "", Line, re.I)
  192             IDCode = IDCode.strip() if len(IDCode.strip()) else "Not Available"
  193             break
  194 
  195     # Classification...
  196     for Line in Lines:
  197         if re.match("^_struct_keywords.pdbx_keywords", Line, re.I):
  198             Classification = re.sub("^_struct_keywords.pdbx_keywords", "", Line, re.I)
  199             Classification = Classification.strip() if len(Classification.strip()) else "Not Available"
  200             break
  201     
  202     # Deposition date...
  203     for Line in Lines:
  204         if re.match("^_pdbx_database_status.recvd_initial_deposition_date", Line, re.I):
  205             DepositionDate = re.sub("^_pdbx_database_status.recvd_initial_deposition_date", "", Line, re.I)
  206             DepositionDate = DepositionDate.strip() if len(DepositionDate.strip()) else "Not Available"
  207             break
  208     
  209     # Experimental technique...
  210     for Line in Lines:
  211         if re.match("^_exptl.method", Line, re.I):
  212             ExperimentalTechnique = re.sub("(_exptl.method|')", "", Line, flags = re.I)
  213             ExperimentalTechnique = ExperimentalTechnique.strip() if len(ExperimentalTechnique.strip()) else "Not Available"
  214             break
  215         
  216     # Resolution...
  217     for Line in Lines:
  218         if re.match("^_reflns.d_resolution_high", Line, re.I):
  219             Resolution = re.sub("^_reflns.d_resolution_high", "", Line, re.I)
  220             Resolution = Resolution.strip() if len(Resolution.strip()) else "Not Available"
  221             break
  222         
  223     return Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution
  224 
  225 def PrepareLineForFormatSize(Text, FormatSize):
  226     """Prepare text string for format size padding or truncation to
  227     alter its size.
  228     """
  229 
  230     TextLen = len(Text)
  231     if TextLen < FormatSize:
  232         PaddingLen = FormatSize - TextLen
  233         TextPadding = " " * PaddingLen
  234         Text = Text + TextPadding
  235     elif TextLen > FormatSize:
  236         Text = Text[:FormatSize]
  237     
  238     return Text
  239 
  240 def ListChainsInfo(MolName, ChainIDs):
  241     """List chains information across all chains."""
  242 
  243     if not (OptionsInfo["All"] or OptionsInfo["Chains"]):
  244         return
  245 
  246     ChainsInfo = ", ".join(ChainIDs) if len(ChainIDs) else "None"
  247 
  248     MiscUtil.PrintInfo("\nNumber of chains: %s" % len(ChainIDs))
  249     MiscUtil.PrintInfo("ChainIDs: %s" % ChainsInfo)
  250 
  251 def ListChainsResiduesInfo(MolName, ChainIDs):
  252     """List polymer chain residue information across all chains."""
  253     
  254     if not (OptionsInfo["All"] or OptionsInfo["CountResidues"]):
  255         return
  256     
  257     ListSelectionResiduesInfo(MolName, ChainIDs, "Chains")
  258 
  259     # List information for non-standard amino acids...
  260     ListSelectionResiduesInfo(MolName, ChainIDs, "NonStandardAminoAcids")
  261 
  262 def ListLigandsInfo(MolName, ChainIDs):
  263     """List ligand information across all chains."""
  264     
  265     if not (OptionsInfo["All"] or OptionsInfo["Ligands"]):
  266         return
  267     
  268     ListSelectionResiduesInfo(MolName, ChainIDs, "Ligands")
  269 
  270 def ListSolventsInfo(MolName, ChainIDs):
  271     """List solvents information across all chains."""
  272     
  273     if not (OptionsInfo["All"] or OptionsInfo["Solvents"]):
  274         return
  275 
  276     ListSelectionResiduesInfo(MolName, ChainIDs, "Solvents")
  277     
  278 def ListInorganicsInfo(MolName, ChainIDs):
  279     """List inorganics information across all chains."""
  280     
  281     if not (OptionsInfo["All"] or OptionsInfo["Inorganics"]):
  282         return
  283     
  284     ListSelectionResiduesInfo(MolName, ChainIDs, "Inorganics")
  285 
  286 def ListSelectionResiduesInfo(MolName, ChainIDs, SelectionType):
  287     """List residues information for a specified selection type. """
  288 
  289     Lines = []
  290     TotalResCount = 0
  291     SelectionLabel = None
  292     
  293     if not len(ChainIDs):
  294         SelectionInfo, SelectionLabel = GetSelectionResiduesInfo(MolName, None, SelectionType)
  295         MiscUtil.PrintInfo("\nNumber of %s residues: %s" % (SelectionLabel, TotalResCount))
  296         return
  297         
  298     for ChainID in ChainIDs:
  299         SelectionInfo, SelectionLabel = GetSelectionResiduesInfo(MolName, ChainID, SelectionType)
  300 
  301         ChainResCount = 0
  302         LineWords = []
  303         
  304         SortedResNames = sorted(SelectionInfo["ResNames"], key = lambda ResName: SelectionInfo["ResCount"][ResName], reverse = True)
  305         for ResName in SortedResNames:
  306             ResCount = SelectionInfo["ResCount"][ResName]
  307             LineWords.append("%s - %s" % (ResName, ResCount))
  308 
  309             ChainResCount += ResCount
  310             TotalResCount += ResCount
  311         
  312         Line = "; ".join(LineWords) if len(LineWords) else None
  313         Lines.append("Chain ID: %s; Count: %s; Names:  %s" % (ChainID, ChainResCount, Line))
  314     
  315     MiscUtil.PrintInfo("\nNumber of %s residues: %s" % (SelectionLabel, TotalResCount))
  316     for Line in Lines:
  317         MiscUtil.PrintInfo("%s" % Line)
  318 
  319 def GetSelectionResiduesInfo(MolName, ChainID, SelectionType):
  320     """Get residues info for a specified selection type. """
  321 
  322     SelectionInfo = None
  323     SelectionLabel = None
  324     
  325     if re.match("^Ligands$", SelectionType, re.I):
  326         SelectionLabel = "ligand"
  327         SelectionInfo = PyMOLUtil.GetLigandResiduesInfo(MolName, ChainID) if ChainID is not None else None
  328     elif re.match("^Solvents$", SelectionType, re.I):
  329         SelectionLabel = "solvent"
  330         SelectionInfo = PyMOLUtil.GetSolventResiduesInfo(MolName, ChainID) if ChainID is not None else None
  331     elif re.match("^Inorganics$", SelectionType, re.I):
  332         SelectionLabel = "inorganic"
  333         SelectionInfo = PyMOLUtil.GetInorganicResiduesInfo(MolName, ChainID) if ChainID is not None else None
  334     elif re.match("^Chains$", SelectionType, re.I):
  335         SelectionLabel = "polymer chain"
  336         SelectionInfo = PyMOLUtil.GetPolymerResiduesInfo(MolName, ChainID) if ChainID is not None else None
  337     elif re.match("^NonStandardAminoAcids$", SelectionType, re.I):
  338         SelectionLabel = "non-standard amino acids"
  339         SelectionInfo = PyMOLUtil.GetAminoAcidResiduesInfo(MolName, ChainID, "NonStandard") if ChainID is not None else None
  340     else:
  341         MiscUtil.PrintError("Failed to retrieve residues information: Selection type %s is not valid..." % SelectionType)
  342 
  343     return SelectionInfo, SelectionLabel
  344 
  345 def ListPocketsInfo(MolName, ChainIDs):
  346     """List pockect residues information across all chains."""
  347     
  348     if not (OptionsInfo["All"] or OptionsInfo["PocketLigands"] or OptionsInfo["PocketSolvents"] or OptionsInfo["PocketInorganics"]) :
  349         return
  350     
  351     for ChainID in ChainIDs:
  352         MiscUtil.PrintInfo("\nListing ligand pockets information for chain %s..." % (ChainID))
  353         
  354         LigandsInfo = PyMOLUtil.GetLigandResiduesInfo(MolName, ChainID)
  355         if not len(LigandsInfo["ResNames"]):
  356             MiscUtil.PrintInfo("\nNumber of residues in ligand pocket: None")
  357             MiscUtil.PrintInfo("Chain ID: %s; Ligands: None" % (ChainID))
  358             continue
  359         
  360         for LigandResName in sorted(LigandsInfo["ResNames"]):
  361             for LigandResNum in LigandsInfo["ResNum"][LigandResName]:
  362                 ListPocketsPolymerInfo(MolName, ChainID, LigandResName, LigandResNum)
  363                 ListPocketsSolventsInfo(MolName, ChainID, LigandResName, LigandResNum)
  364                 ListPocketsInorganicsInfo(MolName, ChainID, LigandResName, LigandResNum)
  365 
  366 def ListPocketsPolymerInfo(MolName, ChainID, LigandResName, LigandResNum):
  367     """List pockect residues information across all chains."""
  368 
  369     if not (OptionsInfo["All"] or OptionsInfo["PocketLigands"]):
  370         return
  371     
  372     ListPocketSelectionResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, "Pockets")
  373 
  374 def ListPocketsSolventsInfo(MolName, ChainID, LigandResName, LigandResNum):
  375     """List pockect solvent residues information across all chains."""
  376     
  377     if not (OptionsInfo["All"] or OptionsInfo["PocketSolvents"]):
  378         return
  379     
  380     ListPocketSelectionResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, "PocketSolvents")
  381 
  382 def ListPocketsInorganicsInfo(MolName, ChainID, LigandResName, LigandResNum):
  383     """List pockect inorganic residues information across all chains."""
  384     
  385     if not (OptionsInfo["All"] or OptionsInfo["PocketInorganics"]):
  386         return
  387     
  388     ListPocketSelectionResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, "PocketInorganics")
  389 
  390 def ListPocketSelectionResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, SelectionType):
  391     """List residues information for a specified pocket selection type. """
  392 
  393     PocketDistanceCutoff  = OptionsInfo["PocketDistanceCutoff"]
  394     SelectionLabel = GetPocketSelectionResiduesInfoLabel(SelectionType)
  395     
  396     SelectionInfo = GetPocketSelectionResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, PocketDistanceCutoff, SelectionType)
  397 
  398     ResiduesCount, ResiduesDistribution, ResiduesIDs = SetupSelectionResiduesInfo(SelectionInfo)
  399     MiscUtil.PrintInfo("\nNumber of %s residues in ligand pocket: %s" % (SelectionLabel, ResiduesCount))
  400     MiscUtil.PrintInfo("Chain ID: %s; Ligand ID: %s_%s\nResidue distribution: %s\nResidue IDs: %s" % (ChainID, LigandResName, LigandResNum, ResiduesDistribution, ResiduesIDs))
  401 
  402 def GetPocketSelectionResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, PocketDistanceCutoff, SelectionType):
  403     """Get pocket residues info for a specified selection type. """
  404 
  405     SelectionInfo = None
  406     
  407     if re.match("^Pockets$", SelectionType, re.I):
  408         SelectionInfo = PyMOLUtil.GetPocketPolymerResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, PocketDistanceCutoff)
  409     elif re.match("^PocketSolvents$", SelectionType, re.I):
  410         SelectionInfo = PyMOLUtil.GetPocketSolventResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, PocketDistanceCutoff)
  411     elif re.match("^PocketInorganics$", SelectionType, re.I):
  412         SelectionInfo = PyMOLUtil.GetPocketInorganicResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, PocketDistanceCutoff)
  413     else:
  414         MiscUtil.PrintError("Failed to retrieve pocket residues information: Selection type %s is not valid..." % SelectionType)
  415 
  416     return SelectionInfo
  417 
  418 def ListSurfaceResiduesInfo(MolName, ChainIDs):
  419     """List surface and buried residues for polymer chains with in a molecule."""
  420     
  421     if not (OptionsInfo["All"] or OptionsInfo["SurfaceResidues"]):
  422         return
  423 
  424     MiscUtil.PrintInfo("\nListing surface and buried residues information...")
  425     if not len(ChainIDs):
  426         MiscUtil.PrintInfo("\nNumber of surface residues: None\nNumber of buried residues: None")
  427         return
  428 
  429     TotalSurfaceResidues, TotalBuriedResidues = [0] * 2
  430     
  431     for ChainID in ChainIDs:
  432         SurfaceResiduesSelectionInfo, BuriedResiduesSelectionInfo = PyMOLUtil.GetSurfaceAndBuriedResiduesInfo(MolName, ChainID, OptionsInfo["SurfaceResiduesCutoff"])
  433         
  434         SurfaceResiduesCount, SurfaceResiduesDistribution, SurfaceResiduesIDs = SetupSelectionResiduesInfo(SurfaceResiduesSelectionInfo)
  435         BuriedResiduesCount, BuriedResiduesDistribution, BuriedResiduesIDs = SetupSelectionResiduesInfo(BuriedResiduesSelectionInfo)
  436 
  437         TotalSurfaceResidues += SurfaceResiduesCount
  438         MiscUtil.PrintInfo("\nChainID: %s; Number of surface residues: %d" % (ChainID, SurfaceResiduesCount))
  439         MiscUtil.PrintInfo("Residue distribution: %s" % (SurfaceResiduesDistribution))
  440         if OptionsInfo["SurfaceResiduesIDs"]:
  441             MiscUtil.PrintInfo("Residue IDs: %s" % (SurfaceResiduesIDs))
  442         
  443         TotalBuriedResidues += BuriedResiduesCount
  444         MiscUtil.PrintInfo("\nChainID: %s; Number of buried residues: %d" % (ChainID, BuriedResiduesCount))
  445         MiscUtil.PrintInfo("Residue distribution: %s" % (BuriedResiduesDistribution))
  446         if OptionsInfo["SurfaceResiduesIDs"]:
  447             MiscUtil.PrintInfo("Residue IDs: %s" % (BuriedResiduesIDs))
  448     
  449     MiscUtil.PrintInfo("\nTotal number of surface residues: %d\nTotal number of buried residues: %s" % (TotalSurfaceResidues, TotalBuriedResidues))
  450         
  451 def ListPhiPsiAnglesInfo(MolName, ChainIDs):
  452     """List phi and psi torsion angles for polymer chains with in a molecule."""
  453     
  454     if not (OptionsInfo["All"] or OptionsInfo["PhiPsi"]):
  455         return
  456 
  457     MiscUtil.PrintInfo("\nListing phi and psi angles information...")
  458     
  459     if not len(ChainIDs):
  460         MiscUtil.PrintInfo("\nNumber of phi and psi angles: None\n")
  461         return
  462 
  463     for ChainID in ChainIDs:
  464         if re.match("^Categories$", OptionsInfo["PhiPsiMode"], re.I):
  465             # Retrieve phi and psi angles by categories used for Ramachandran plots
  466             GeneralPhiPsiInfo, GlyPhiPsiInfo, ProPhiPsiInfo, PreProPhiPsiInfo = PyMOLUtil.GetPhiPsiCategoriesResiduesInfo(MolName, ChainID)
  467             
  468             SetupAndListPhiPsiResiduesInfo(GeneralPhiPsiInfo, "General (All residues except glycine, proline, or pre-proline)", ChainID)
  469             SetupAndListPhiPsiResiduesInfo(GlyPhiPsiInfo, "Glycine (Only glycine residues)", ChainID)
  470             SetupAndListPhiPsiResiduesInfo(ProPhiPsiInfo, "Proline (Only proline residues)", ChainID)
  471             SetupAndListPhiPsiResiduesInfo(PreProPhiPsiInfo, "Pre-Proline (Only residues before proline not including glycine or proline)", ChainID)
  472         else:
  473             PhiPsiResiduesInfo = PyMOLUtil.GetPhiPsiResiduesInfo(MolName, ChainID, Categorize = True)
  474             SetupAndListPhiPsiResiduesInfo(PhiPsiResiduesInfo, "All", ChainID)
  475 
  476 def SetupAndListPhiPsiResiduesInfo(PhiPsiResiduesInfo, AnglesType, ChainID):
  477     """Setup and  list information for phi and psi torsion angles."""
  478 
  479     MiscUtil.PrintInfo("\nChainID: %s; Phi and Psi angles: %s" %(ChainID, AnglesType))
  480 
  481     PhiPsiCount = len(PhiPsiResiduesInfo["ResNums"])
  482     if not PhiPsiCount:
  483         MiscUtil.PrintInfo("Number of angles: None")
  484         return
  485 
  486     MiscUtil.PrintInfo("Number of phi and psi angles: %d" % PhiPsiCount)
  487 
  488     CategoriesMode = True if re.match("^Categories$", OptionsInfo["PhiPsiMode"], re.I) else False
  489 
  490     if CategoriesMode:
  491         MiscUtil.PrintInfo("\nResNum, ResName, Phi, Psi")
  492     else:
  493         MiscUtil.PrintInfo("\nResNum, ResName, Phi, Psi, Category")
  494     
  495     Precision = OptionsInfo["PhiPsiPrecision"]
  496     for ResNum in PhiPsiResiduesInfo["ResNums"]:
  497         ResName = PhiPsiResiduesInfo["ResName"][ResNum]
  498         Phi = PhiPsiResiduesInfo["Phi"][ResNum]
  499         Psi = PhiPsiResiduesInfo["Psi"][ResNum]
  500         if CategoriesMode:
  501             MiscUtil.PrintInfo("%s, %s, %.*f, %.*f" % (ResNum, ResName, Precision, Phi, Precision, Psi))
  502         else:
  503             Category = PhiPsiResiduesInfo["Category"][ResNum]
  504             MiscUtil.PrintInfo("%s, %s, %.*f, %.*f, %s " % (ResNum, ResName, Precision, Phi, Precision, Psi, Category))
  505 
  506 def ListInterfaceResiduesInfo(MolName, ChainIDs):
  507     """List interface residues between all unique pairs of polymer chains
  508     with in a molecule."""
  509     
  510     if not (OptionsInfo["All"] or OptionsInfo["InterfaceResidues"]):
  511         return
  512 
  513     MiscUtil.PrintInfo("\nListing interface residues information...")
  514 
  515     # Get chain ID pairs for identifying interface residues...
  516     ChainIDsPairs = GetChainIDsPairsForInterfaceResidues(ChainIDs)
  517     if not len(ChainIDsPairs):
  518         MiscUtil.PrintInfo("Valid interface chain ID pairs: None")
  519         return
  520 
  521     InterfaceResiduesMethod = OptionsInfo["InterfaceResiduesMethod"]
  522     InterfaceResiduesCutoff = OptionsInfo["InterfaceResiduesCutoff"]
  523     MiscUtil.PrintInfo("Methodology: %s; Cutoff: %.2f" % (InterfaceResiduesMethod, InterfaceResiduesCutoff))
  524     
  525     for ChainIDsPair in ChainIDsPairs:
  526         InterfaceChainIDs1, InterfaceChainIDs2 = ChainIDsPair
  527         InterfaceID = "%s-%s" % ("+".join(InterfaceChainIDs1), "+".join(InterfaceChainIDs2))
  528 
  529         InterfaceResiduesInfo1, InterfaceResiduesInfo2 = GetInterfaceChainsAndResiduesInfo(MolName, InterfaceChainIDs1, MolName, InterfaceChainIDs2, InterfaceResiduesMethod, InterfaceResiduesCutoff)
  530 
  531         ChainNames1, ResiduesInfo1 = SetupSelectionChainsResiduesInfo(InterfaceResiduesInfo1)
  532         ChainNames2, ResiduesInfo2 = SetupSelectionChainsResiduesInfo(InterfaceResiduesInfo2)
  533         
  534         if len(ChainNames1) and len(ChainNames2):
  535             MiscUtil.PrintInfo("\nListing interface residues for interface chain IDs: %s" % (InterfaceID))
  536             
  537             ListInterfaceChainsAndResiduesInfo(InterfaceID,  InterfaceChainIDs1, ChainNames1, ResiduesInfo1)
  538             ListInterfaceChainsAndResiduesInfo(InterfaceID,  InterfaceChainIDs2, ChainNames2, ResiduesInfo2)
  539         else:
  540             MiscUtil.PrintInfo("\nListing interface residues for interface chain IDs: %s" % (InterfaceID))
  541             MiscUtil.PrintInfo("Interface chain IDs: None; ChainID: None; Number of interface residues: 0")
  542 
  543 def ListInterfaceChainsAndResiduesInfo(InterfaceID, InterfaceChainIDs, ChainNames, ResiduesInfo):
  544     """List interface chains and residues. """
  545 
  546     for ChainID in InterfaceChainIDs:
  547         if not ChainID in ChainNames:
  548             MiscUtil.PrintInfo("\nInterface chain IDs: %s; ChainID: %s; Number of interface residues: 0" % (InterfaceID, ChainID))
  549             continue
  550         
  551         for Index in range(0, len(ChainNames)):
  552             ChainName = ChainNames[Index]
  553             ChainResiduesInfo = ResiduesInfo[Index]
  554             if re.match(ChainID, ChainName, re.I):
  555                 MiscUtil.PrintInfo("\nInterface chain IDs: %s; ChainID: %s; Number of interface residues: %d" % (InterfaceID, ChainName, ChainResiduesInfo[0]))
  556                 MiscUtil.PrintInfo("Residue distribution: %s\nResidue IDs: %s" % (ChainResiduesInfo[1], ChainResiduesInfo[2]))
  557                 continue
  558 
  559 def GetInterfaceChainsAndResiduesInfo(MolName1, ChainIDs1, MolName2, ChainIDs2, Method, Cutoff):
  560     """Get interface chains and residues info for chains using a specified methodology."""
  561 
  562     InterfaceChainsResiduesInfo1 = None
  563     InterfaceChainsResiduesInfo2 = None
  564 
  565     ChainNames1 = ",".join(ChainIDs1)
  566     ChainNames2 = ",".join(ChainIDs2)
  567     
  568     if re.match("^BySASAChange$", Method, re.I):
  569         InterfaceChainsResiduesInfo1, InterfaceChainsResiduesInfo2 = PyMOLUtil.GetInterfaceChainsResiduesBySASAChange(MolName1, ChainNames1, MolName2, ChainNames2, Cutoff)
  570     elif re.match("^ByHeavyAtomsDistance$", Method, re.I):
  571         InterfaceChainsResiduesInfo1, InterfaceChainsResiduesInfo2 = PyMOLUtil.GetnterfaceChainsResiduesByHeavyAtomsDistance(MolName1, ChainNames1, MolName2, ChainNames2, Cutoff)
  572     elif re.match("^ByCAlphaAtomsDistance$", Method, re.I):
  573         InterfaceChainsResiduesInfo1, InterfaceChainsResiduesInfo2 = PyMOLUtil.GetInterfaceChainsResiduesByCAlphaAtomsDistance(MolName1, ChainNames1, MolName2, ChainNames2, Cutoff)
  574     else:
  575         MiscUtil.PrintError("Failed to retrieve interface residues information: Method %s is not valid..." % Method)
  576 
  577     return InterfaceChainsResiduesInfo1, InterfaceChainsResiduesInfo2
  578 
  579 def GetChainIDsPairsForInterfaceResidues(ChainIDs):
  580     """Get chain IDs pairs for identifying interface residues. """
  581 
  582     ChainIDsPairsList = []
  583     
  584     InterfaceResiduesChainsList = OptionsInfo["InterfaceResiduesChainsList"]
  585     if not len(InterfaceResiduesChainsList):
  586         # Use first two chain IDs...
  587         if len(ChainIDs) >= 2:
  588             ChainIDsPair = [ChainIDs[0], ChainIDs[1]]
  589             ChainIDsPairsList.append(ChainIDsPair)
  590         return ChainIDsPairsList
  591 
  592     # Validate specified pairwise chain IDs...
  593     for Index in range(0, len(InterfaceResiduesChainsList), 2):
  594         ChainIDs1 = InterfaceResiduesChainsList[Index]
  595         ChainIDs2 = InterfaceResiduesChainsList[Index + 1]
  596         
  597         ValidChainIDs = True
  598         SpecifiedChainIDs = []
  599         SpecifiedChainIDs.extend(ChainIDs1)
  600         SpecifiedChainIDs.extend(ChainIDs2)
  601         
  602         for ChainID in (SpecifiedChainIDs):
  603             if not ChainID in ChainIDs:
  604                 ValidChainIDs = False
  605                 MiscUtil.PrintWarning("The chain ID, %s, specified using \"--interfaceResiduesChains\" for a chain IDs pairs is not a valid chain ID." % (ChainID))
  606         
  607         if not ValidChainIDs:
  608             MiscUtil.PrintWarning("Ignoring chain IDs pair: %s, %s" % ("+".join(ChainIDs1), "+".join(ChainIDs2)))
  609             continue
  610         
  611         ChainIDsPair = [ChainIDs1, ChainIDs2]
  612         ChainIDsPairsList.append(ChainIDsPair)
  613 
  614     return ChainIDsPairsList
  615     
  616 def SetupSelectionResiduesInfo(SelectionInfo):
  617     """Setup residues info."""
  618     
  619     # Setup distribution of residues...
  620     LineWords = []
  621     ResiduesCount = 0
  622     SortedResNames = sorted(SelectionInfo["ResNames"], key = lambda ResName: SelectionInfo["ResCount"][ResName], reverse = True)
  623     for ResName in SortedResNames:
  624         ResCount = SelectionInfo["ResCount"][ResName]
  625         LineWords.append("%s - %s" % (ResName, ResCount))
  626         ResiduesCount += ResCount
  627     
  628     ResiduesDistribution = "; ".join(LineWords) if len(LineWords) else None
  629     
  630     # Setup residue IDs sorted by residue numbers...
  631     ResNumMap = {}
  632     for ResName in SelectionInfo["ResNames"]:
  633         for ResNum in SelectionInfo["ResNum"][ResName]:
  634             ResNumMap[ResNum] = ResName
  635     
  636     LineWords = []
  637     for ResNum in sorted(ResNumMap, key = int):
  638         ResName = ResNumMap[ResNum]
  639         ResID = "%s_%s" % (ResName, ResNum)
  640         LineWords.append(ResID)
  641     ResiduesIDs = ", ".join(LineWords) if len(LineWords) else None
  642                 
  643     return ResiduesCount, ResiduesDistribution, ResiduesIDs
  644 
  645 def SetupSelectionChainsResiduesInfo(SelectionInfo):
  646     """Setup chains and residues info."""
  647 
  648     ChainNames = []
  649     ResiduesInfo = []
  650 
  651     for ChainID in SelectionInfo["ChainIDs"]:
  652         ChainNames.append(ChainID)
  653         
  654         # Setup distribution of residues...
  655         LineWords = []
  656         ResiduesCount = 0
  657         SortedResNames = sorted(SelectionInfo["ResNames"][ChainID], key = lambda ResName: SelectionInfo["ResCount"][ChainID][ResName], reverse = True)
  658         for ResName in SortedResNames:
  659             ResCount = SelectionInfo["ResCount"][ChainID][ResName]
  660             LineWords.append("%s - %s" % (ResName, ResCount))
  661             ResiduesCount += ResCount
  662         
  663         ResiduesDistribution = "; ".join(LineWords) if len(LineWords) else None
  664         
  665         # Setup residue IDs sorted by residue numbers...
  666         ResNumMap = {}
  667         for ResName in SelectionInfo["ResNames"][ChainID]:
  668             for ResNum in SelectionInfo["ResNum"][ChainID][ResName]:
  669                 ResNumMap[ResNum] = ResName
  670         
  671         LineWords = []
  672         for ResNum in sorted(ResNumMap, key = int):
  673             ResName = ResNumMap[ResNum]
  674             ResID = "%s_%s" % (ResName, ResNum)
  675             LineWords.append(ResID)
  676         ResiduesIDs = ", ".join(LineWords) if len(LineWords) else None
  677 
  678         ResiduesInfo.append([ResiduesCount, ResiduesDistribution, ResiduesIDs])
  679                 
  680     return ChainNames, ResiduesInfo
  681 
  682 def ListBoundingBoxInfo(MolName):
  683     """List bounding box information. """
  684     
  685     if not (OptionsInfo["All"] or OptionsInfo["BoundingBox"]):
  686         return
  687 
  688     MolSelection = "(%s)" % MolName
  689     MolExtents = pymol.cmd.get_extent(MolSelection)
  690     
  691     XMin, YMin, ZMin = MolExtents[0]
  692     XMax, YMax, ZMax = MolExtents[1]
  693 
  694     XSize = abs(XMax - XMin)
  695     YSize = abs(YMax - YMin)
  696     ZSize = abs(ZMax - ZMin)
  697 
  698     MiscUtil.PrintInfo("\nBounding box coordinates: <XMin, XMax> - <%.3f, %.3f>; <YMin, YMax> - <%.3f, %.3f>; <ZMin, ZMax> - <%.3f, %.3f>" % (XMin, XMax, YMin, YMax, ZMin, ZMax))
  699     MiscUtil.PrintInfo("Bounding box size in Angstroms: XSize - %.3f; YSize - %.3f; ZSize - %.3f" % (XSize, YSize, ZSize))
  700     
  701 def ListFileSizeAndModificationInfo(Infile):
  702     """List file size and modification time info."""
  703 
  704     MiscUtil.PrintInfo("\nFile size: %s" % MiscUtil.GetFormattedFileSize(Infile))
  705     MiscUtil.PrintInfo("Last modified: %s" % time.ctime(os.path.getmtime(Infile)))
  706     MiscUtil.PrintInfo("Created: %s" % time.ctime(os.path.getctime(Infile)))
  707     
  708 def GetPocketSelectionResiduesInfoLabel(SelectionType):
  709     """Get pocket residues info label for a specified selection type. """
  710 
  711     SelectionLabel = None
  712     
  713     if re.match("^Pockets$", SelectionType, re.I):
  714         SelectionLabel = "polymer"
  715     elif re.match("^PocketSolvents$", SelectionType, re.I):
  716         SelectionLabel = "solvent"
  717     elif re.match("^PocketInorganics$", SelectionType, re.I):
  718         SelectionLabel = "inorganic"
  719     else:
  720         MiscUtil.PrintError("Failed to retrieve pocket residues label information: Selection type %s is not valid..." % SelectionType)
  721 
  722     return SelectionLabel
  723 
  724 def ProcessOptions():
  725     """Process and validate command line arguments and options"""
  726 
  727     MiscUtil.PrintInfo("Processing options...")
  728     
  729     # Validate options...
  730     ValidateOptions()
  731 
  732     OptionsInfo["All"] = Options["--all"]
  733     OptionsInfo["BoundingBox"] = Options["--boundingBox"]
  734     
  735     OptionsInfo["Chains"] = Options["--chains"]
  736     
  737     OptionsInfo["CountResidues"] = Options["--countResidues"]
  738     OptionsInfo["Header"] = Options["--header"]
  739     
  740     OptionsInfo["Infiles"] = Options["--infiles"]
  741     OptionsInfo["InfilesNames"] =  Options["--infilesNames"]
  742     
  743     OptionsInfo["Inorganics"] = Options["--inorganics"]
  744 
  745     OptionsInfo["InterfaceResidues"] = Options["--interfaceResidues"]
  746     OptionsInfo["InterfaceResiduesMethod"] = Options["--interfaceResiduesMethod"]
  747     
  748     InterfaceResiduesChains = Options["--interfaceResiduesChains"]
  749     InterfaceResiduesChainsList = []
  750     if not re.match("^auto$", InterfaceResiduesChains, re.I):
  751         InterfaceResiduesChains = re.sub(" ", "", Options["--interfaceResiduesChains"])
  752         InterfaceResiduesChainsWords = InterfaceResiduesChains.split(",")
  753         if len(InterfaceResiduesChainsWords) % 2:
  754             MiscUtil.PrintError("The number of comma delimited chain IDs, %d, specified using \"--interfaceResiduesChains\" option, \"%s\",  must be a multple of 2." % (len(InterfaceResiduesChainsWords), Options["--interfaceResiduesChains"]))
  755         for ChainID in InterfaceResiduesChainsWords:
  756             ChainIDWords = ChainID.split("+")
  757             InterfaceResiduesChainsList.append(ChainIDWords)
  758     OptionsInfo["InterfaceResiduesChains"] = InterfaceResiduesChains
  759     OptionsInfo["InterfaceResiduesChainsList"] = InterfaceResiduesChainsList
  760     
  761     InterfaceResiduesCutoff = Options["--interfaceResiduesCutoff"]
  762     InterfaceResiduesMethod = OptionsInfo["InterfaceResiduesMethod"]
  763     if re.match("^auto$", InterfaceResiduesCutoff, re.I):
  764         if re.match("^BySASAChange$", InterfaceResiduesMethod, re.I):
  765             InterfaceResiduesCutoff = 1.0
  766         elif re.match("^ByHeavyAtomsDistance$", InterfaceResiduesMethod, re.I):
  767             InterfaceResiduesCutoff = 5.0
  768         elif re.match("^ByCAlphaAtomsDistance$", InterfaceResiduesMethod, re.I):
  769             InterfaceResiduesCutoff = 8.0
  770         else:
  771             MiscUtil.PrintError("The specified value, %s, for option \"--interfaceResiduesMethod\" is not supported." % (InterfaceResiduesMethod))
  772     else:
  773         InterfaceResiduesCutoff = float(InterfaceResiduesCutoff)
  774     OptionsInfo["InterfaceResiduesCutoff"] = InterfaceResiduesCutoff
  775     
  776     OptionsInfo["Ligands"] = Options["--ligands"]
  777     
  778     OptionsInfo["PocketLigands"] = Options["--pocketLigands"]
  779     OptionsInfo["PocketDistanceCutoff"] = float(Options["--pocketDistanceCutoff"])
  780     OptionsInfo["PocketSolvents"] = Options["--pocketSolvents"]
  781     OptionsInfo["PocketInorganics"] = Options["--pocketInorganics"]
  782     
  783     OptionsInfo["PhiPsi"] = Options["--phiPsi"]
  784     OptionsInfo["PhiPsiMode"] = Options["--phiPsiMode"]
  785     OptionsInfo["PhiPsiPrecision"] = int(Options["--phiPsiPrecision"])
  786     
  787     OptionsInfo["Solvents"] = Options["--solvents"]
  788     
  789     OptionsInfo["SurfaceResidues"] = Options["--surfaceResidues"]
  790     OptionsInfo["SurfaceResiduesCutoff"] = float(Options["--surfaceResiduesCutoff"])
  791     OptionsInfo["SurfaceResiduesIDs"] = True if re.match("^Yes$", Options["--surfaceResiduesIDs"], re.I) else False
  792 
  793     # Always list header, chains, and ligands...
  794     OptionNames = ["Chains", "Header", "Ligands"]
  795     for Name in OptionNames:
  796         if Name in OptionsInfo:
  797             OptionsInfo[Name] = True
  798         else:
  799             MiscUtil.PrintError("Option name %s is not a valid name..." % s)
  800     
  801 def RetrieveOptions(): 
  802     """Retrieve command line arguments and options"""
  803     
  804     # Get options...
  805     global Options
  806     Options = docopt(_docoptUsage_)
  807 
  808     # Set current working directory to the specified directory...
  809     WorkingDir = Options["--workingdir"]
  810     if WorkingDir:
  811         os.chdir(WorkingDir)
  812     
  813     # Handle examples option...
  814     if "--examples" in Options and Options["--examples"]:
  815         MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_))
  816         sys.exit(0)
  817     
  818 def ValidateOptions():
  819     """Validate option values"""
  820 
  821     # Expand infile names..
  822     InfilesNames = MiscUtil.ExpandFileNames(Options["--infiles"], ",")
  823 
  824     # Validate file extensions...
  825     for Infile in InfilesNames:
  826         MiscUtil.ValidateOptionFilePath("-i, --infiles", Infile)
  827         MiscUtil.ValidateOptionFileExt("-i, --infiles", Infile, "pdb cif")
  828     Options["--infilesNames"] = InfilesNames
  829     
  830     if not re.match("^auto$", Options["--interfaceResiduesCutoff"], re.I):
  831         MiscUtil.ValidateOptionFloatValue("--interfaceResiduesCutoff", Options["--interfaceResiduesCutoff"], {">": 0.0})
  832     MiscUtil.ValidateOptionTextValue("--interfaceResiduesMethod", Options["--interfaceResiduesMethod"], "BySASAChange ByHeavyAtomsDistance ByCAlphaAtomsDistance")
  833     
  834     MiscUtil.ValidateOptionFloatValue("--pocketDistanceCutoff", Options["--pocketDistanceCutoff"], {">": 0.0})
  835     MiscUtil.ValidateOptionTextValue("--phiPsiMode", Options["--phiPsiMode"], "All  Categories")
  836     MiscUtil.ValidateOptionIntegerValue("--phiPsiPrecision", Options["--phiPsiPrecision"], {">": 0})
  837     MiscUtil.ValidateOptionFloatValue("--surfaceResiduesCutoff", Options["--surfaceResiduesCutoff"], {">": 0.0})
  838     MiscUtil.ValidateOptionTextValue("--surfaceResiduesIDs", Options["--surfaceResiduesIDs"], "Yes No")
  839 
  840 # Setup a usage string for docopt...
  841 _docoptUsage_ = """
  842 PyMOLInfoMacromolecules.py - List information about macromolecules
  843 
  844 Usage:
  845     PyMOLInfoMacromolecules.py [--all] [--boundingBox] [--chains] [--countResidues] 
  846                                [--header] [--inorganics] [--interfaceResidues]
  847                                [--interfaceResiduesChains <ChainID1,ChainD2,...>] [--interfaceResiduesMethod <text>]
  848                                [--interfaceResiduesCutoff <number>] [--ligands] [--pocketLigands]
  849                                [--pocketDistanceCutoff  <number>] [--pocketSolvents] [--pocketInorganics]
  850                                [--phiPsi] [--phiPsiMode <All or Categories>] [--phiPsiPrecision <number>]
  851                                [--surfaceResidues] [--surfaceResiduesCutoff <number>] [--surfaceResiduesIDs <yes or no>]
  852                                [--solvents] [-w <dir>] -i <infile1,infile2,infile3...>
  853     PyMOLInfoMacromolecules.py -h | --help | -e | --examples
  854 
  855 Description:
  856     List information regarding  ID, classification, experimental technique, chains,
  857     solvents, inorganics, ligands, and ligand binding pockets in macromolecules
  858     present including proteins and nucleic acids.
  859 
  860     The supported input  file format are: PDB (.pdb), mmCIF (.cif)
  861 
  862 Options:
  863     -a, --all
  864         All available information.
  865     -b, --boundingBox
  866         Min and max coordinates for bounding box along with its size.
  867     -c, --chains
  868         Number of chains and their IDs. This is also default behavior.
  869      --countResidues
  870         Number of residues across chains. The chain residues are identified
  871         using polymer selection operator available in PyMOL. In addition,
  872         the non-standard amino acid residues are listed.
  873     -e, --examples
  874         Print examples.
  875     -h, --help
  876         Print this help message.
  877      --header
  878         Header information including experimental technique information
  879         along with any available resolution. This is also default behavior.
  880     -i, --infiles <infile1,infile2,infile3...>
  881         A comma delimited list of input files. The wildcards are also allowed
  882         in file names.
  883     --inorganics
  884         Inorganic residues across chains. The inorganic residues are identified
  885         using inorganic selection operator available in PyMOL.
  886     --interfaceResidues
  887         Interface residues between specified pairs of chains.
  888     --interfaceResiduesChains <ChainID1,Chain1D2,...>  [default: Auto]
  889         Pairwise comma delimited list of chain IDs for the identification of
  890         interface residues. Each chain ID may contain mutiple chain IDs
  891         delimited by a plus sign. For example: A+B,C+D chain pair specifies
  892         interface between chain complexes A+B and C+D.
  893         
  894         The interface residues are identified between first two chains in
  895         input files by default.
  896     --interfaceResiduesMethod <text>  [default: BySASAChange]
  897         Methodology for the identification of interface residues between a pair
  898         of chains in an input file. The interface residues may be identified by
  899         change in solvent accessible surface area (SASA) for a residue between
  900         a chain and chains complex, distance between heavy atoms
  901         in two chains, or distance between CAlpha atoms. Possible values:
  902         BySASAChange, ByHeavyAtomsDistance, or ByCAlphaAtomsDistance. 
  903     --interfaceResiduesCutoff <number>  [default: auto]
  904         Cutoff value used by different methodologies during identification of
  905         interface residues between a pair of chains. The default values are
  906         shown below:
  907         
  908             BySASAChange: 1.0; Units: Angstrom**2 [ Ref 141 ]
  909             ByHeavyAtomsDistance: 5.0; Units: Angstrom [ Ref 142 ]
  910             ByCAlphaAtomsDistance: 8.0; Units: Angstrom [ Ref 143 ]
  911         
  912     -l, --ligands
  913         Ligands across chains. This is also default behavior. The ligands
  914         residues are identified using organic selection operator available
  915         in PyMOL.
  916     -p, --pocketLigands
  917         Chain residues in ligand pockets.
  918     --pocketDistanceCutoff <number>  [default: 5.0]
  919         Distance in Angstroms for identifying pocket residues around ligands.
  920     --pocketSolvents
  921         Solvent residues in ligand pockets. The solvent residues are identified
  922         using solvent selection operator available in PyMOL.
  923     --pocketInorganics
  924         Inorganic residues in ligand pockets. The inorganic residues are identified
  925         using Inorganic selection operator available in PyMOL.
  926      --phiPsi
  927         Phi and psi torsion angles across chains in macromolecules containing
  928         amino acids.
  929      --phiPsiMode <All or Categories>  [default: Categories]
  930         List all phi and psi torsion angles for residues as a single group or split
  931         them into the following categories corresponding to four types of
  932         Ramachandran plots:
  933         
  934             General: All residues except glycine, proline, or pre-proline
  935             Glycine: Only glycine residues
  936             Proline: Only proline residues
  937             Pre-Proline: Only residues before proline not including glycine
  938                 or proline
  939         
  940      --phiPsiPrecision <number>  [default: 2]
  941         Precision for listing phi and psi torsion angles.
  942     -s, --solvents
  943         Solvent residues across chains. The solvent residues are identified
  944         using solvent selection operator available in PyMOL.
  945     --surfaceResidues
  946         Surface and buried residues in chains.
  947     --surfaceResiduesCutoff <number>  [default: 2.5]
  948         Solvenet Accessible Surface Area (SASA) cutoff value in Angstroms**2
  949         for surface and buried resiudes in chains. The residues with SASA less than
  950         the cutoff value correspond to burried residues.
  951     --surfaceResiduesIDs <yes or no>  [default: No]
  952         List residue IDs for surface and buried residues during listing of the
  953         distribution of these residues for '--surfaceResidues' option.
  954     -w, --workingdir <dir>
  955         Location of working directory which defaults to the current directory.
  956 
  957 Examples:
  958     To list header, chains, and ligand information for macromolecules in input
  959     file, type:
  960 
  961         % PyMOLInfoMacromolecules.py  -i Sample3.pdb
  962 
  963     To list all available information for macromolecules in input files, type:
  964 
  965         % PyMOLInfoMacromolecules.py  -a  -i "Sample3.pdb,Sample4.pdb"
  966 
  967     To list pockets residues information along with other default information
  968     for marcomolecules in input file, type:
  969 
  970         % PyMOLInfoMacromolecules.py  -p --pocketDistanceCutoff 4.5 
  971         --pocketSolvents  --pocketInorganics -i Sample3.pdb
  972 
  973     To list chain residues information along with other default information
  974     for marcomolecules in input file, type:
  975 
  976         % PyMOLInfoMacromolecules.py  -c --countResidues --solvents
  977         --inorganics -i "Sample3.pdb,Sample4.pdb" 
  978 
  979     To list interface residues between first two chains by SASA change for
  980     marcomolecules in input file, type:
  981 
  982         % PyMOLInfoMacromolecules.py  --interfaceResidues
  983         -i Sample3.pdb
  984 
  985     To list interface residues between chains E and I  by heay atoms
  986     distance for marcomolecules in input file, type:
  987 
  988         % PyMOLInfoMacromolecules.py  --interfaceResidues
  989         --interfaceResiduesChains E,I  --interfaceResiduesMethod
  990         ByHeavyAtomsDistance --interfaceResiduesCutoff  5 -i Sample3.pdb
  991 
  992     To list interface residues between two sets of chains by SASA change for
  993     marcomolecules in input file, type:
  994 
  995         % PyMOLInfoMacromolecules.py  --interfaceResidues
  996         --interfaceResiduesChains "A+B,C+D" -i Sample8.pdb 
  997 
  998 Author:
  999     Manish Sud(msud@san.rr.com)
 1000 
 1001 See also:
 1002     DownloadPDBFiles.pl, PyMOLSplitChainsAndLigands.py,
 1003     PyMOLVisualizeMacromolecules.py
 1004 
 1005 Copyright:
 1006     Copyright (C) 2019 Manish Sud. All rights reserved.
 1007 
 1008     The functionality available in this script is implemented using PyMOL, a
 1009     molecular visualization system on an open source foundation originally
 1010     developed by Warren DeLano.
 1011 
 1012     This file is part of MayaChemTools.
 1013 
 1014     MayaChemTools is free software; you can redistribute it and/or modify it under
 1015     the terms of the GNU Lesser General Public License as published by the Free
 1016     Software Foundation; either version 3 of the License, or (at your option) any
 1017     later version.
 1018 
 1019 """
 1020 
 1021 if __name__ == "__main__":
 1022     main()