       1 #!/bin/env python
       2 #
       3 # File: PyMOLInfoMacromolecules.py
       4 # Author: Manish Sud <msud@san.rr.com>
       5 #
       6 # Copyright (C) 2018 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()[1], 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     ListBoundingBoxInfo(MolName)
     112
     113     # Delete infile object...
     114     pymol.cmd.delete(MolName)
     115
     116     ListFileSizeAndModificationInfo(Infile)
     117
     118 def ListHeaderInfo(Infile):
     119     """List header information."""
     120
     121     if not OptionsInfo["Header"]:
     122         return
     123
     124     FileDir, FileName, FileExt = MiscUtil.ParseFileName(Infile)
     125
     126     Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution = ["Not Available"] * 5
     127     if re.match("^pdb$", FileExt, re.I):
     128          Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution = RetriveHeadAndExperimentalInfoFromPDBFile(Infile)
     129     elif re.match("^cif$", FileExt, re.I):
     130          Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution = RetriveHeadAndExperimentalInfoFromCIFFile(Infile)
     131
     132     MiscUtil.PrintInfo("\nID: %s\nClassification: %s\nDeposition date: %s" % (IDCode, Classification, DepositionDate))
     133     MiscUtil.PrintInfo("\nExperimental technique %s\nResolution: %s" % (ExperimentalTechnique, Resolution))
     134
     135 def RetriveHeadAndExperimentalInfoFromPDBFile(Infile):
     136     """Retrieve header and experimental information from PDB file. """
     137
     138     Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution = ["Not Available"] * 5
     139
     140     Lines = MiscUtil.GetTextLines(Infile)
     141
     142     # Retrieve header info...
     143     for Line in Lines:
     144         if re.match("^HEADER", Line, re.I):
     145             # Format: 10x40s9s3x4s
     146             FormatSize = 66
     147             Line = PrepareLineForFormatSize(Line, FormatSize)
     148
     149             Classification = Line[10:50]
     150             DepositionDate = Line[50:59]
     151             IDCode = Line[62:66]
     152
     153             Classification = Classification.strip() if len(Classification.strip()) else "Not Available"
     154             DepositionDate = DepositionDate.strip() if len(DepositionDate.strip()) else "Not Available"
     155             IDCode = IDCode.strip() if len(IDCode.strip()) else "Not Available"
     156
     157             break
     158
     159     # Retrieve experimental info...
     160     for Line in Lines:
     161         if re.match("^EXPDTA", Line, re.I):
     162             ExperimentalTechnique = re.sub("^EXPDTA", "", Line, re.I)
     163             ExperimentalTechnique = ExperimentalTechnique.strip()
     164         elif re.match("^REMARK   2 RESOLUTION.", Line, re.I):
     165             if re.search("NOT APPLICABLE", Line, re.I):
     166                 Resolution = "NOT APPLICABLE"
     167             else:
     168                 FormatSize = 70
     169                 Line = PrepareLineForFormatSize(Line, FormatSize)
     170                 Resolution = Line[22:70]
     171                 Resolution = Resolution.strip() if len(Resolution.strip()) else "Not Available"
     172         elif re.match("^(ATOM|HETATM)", Line, re.I):
     173             break
     174
     175     return Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution
     176
     177 def RetriveHeadAndExperimentalInfoFromCIFFile(Infile):
     178     """Retrieve header information from CIF file. """
     179
     180     Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution = ["Not Available"] * 5
     181
     182     Lines = MiscUtil.GetTextLines(Infile)
     183
     184     # IDCode...
     185     for Line in Lines:
     186         if re.match("^_struct_keywords.entry_id", Line, re.I):
     187             IDCode = re.sub("^_struct_keywords.entry_id", "", Line, re.I)
     188             IDCode = IDCode.strip() if len(IDCode.strip()) else "Not Available"
     189             break
     190
     191     # Classification...
     192     for Line in Lines:
     193         if re.match("^_struct_keywords.pdbx_keywords", Line, re.I):
     194             Classification = re.sub("^_struct_keywords.pdbx_keywords", "", Line, re.I)
     195             Classification = Classification.strip() if len(Classification.strip()) else "Not Available"
     196             break
     197
     198     # Deposition date...
     199     for Line in Lines:
     200         if re.match("^_pdbx_database_status.recvd_initial_deposition_date", Line, re.I):
     201             DepositionDate = re.sub("^_pdbx_database_status.recvd_initial_deposition_date", "", Line, re.I)
     202             DepositionDate = DepositionDate.strip() if len(DepositionDate.strip()) else "Not Available"
     203             break
     204
     205     # Experimental technique...
     206     for Line in Lines:
     207         if re.match("^_exptl.method", Line, re.I):
     208             ExperimentalTechnique = re.sub("(_exptl.method|')", "", Line, flags = re.I)
     209             ExperimentalTechnique = ExperimentalTechnique.strip() if len(ExperimentalTechnique.strip()) else "Not Available"
     210             break
     211
     212     # Resolution...
     213     for Line in Lines:
     214         if re.match("^_reflns.d_resolution_high", Line, re.I):
     215             Resolution = re.sub("^_reflns.d_resolution_high", "", Line, re.I)
     216             Resolution = Resolution.strip() if len(Resolution.strip()) else "Not Available"
     217             break
     218
     219     return Classification, DepositionDate, IDCode, ExperimentalTechnique, Resolution
     220
     221 def PrepareLineForFormatSize(Text, FormatSize):
     222     """Prepare text string for format size padding or truncation to
     223     alter its size.
     224     """
     225
     226     TextLen = len(Text)
     227     if TextLen < FormatSize:
     228         PaddingLen = FormatSize - TextLen
     229         TextPadding = " " * PaddingLen
     230         Text = Text + TextPadding
     231     elif TextLen > FormatSize:
     232         Text = Text[:FormatSize]
     233
     234     return Text
     235
     236 def ListChainsInfo(MolName, ChainIDs):
     237     """List chains information across all chains."""
     238
     239     if not OptionsInfo["Chains"]:
     240         return
     241
     242     ChainsInfo = ", ".join(ChainIDs) if len(ChainIDs) else "None"
     243
     244     MiscUtil.PrintInfo("\nNumber of chains: %s" % len(ChainIDs))
     245     MiscUtil.PrintInfo("ChainIDs: %s" % ChainsInfo)
     246
     247 def ListChainsResiduesInfo(MolName, ChainIDs):
     248     """List polymer chain residue information across all chains."""
     249
     250     if not OptionsInfo["CountResidues"]:
     251         return
     252
     253     ListSelectionResiduesInfo(MolName, ChainIDs, "Chains")
     254
     255     # List information for non-standard amino acids...
     256     ListSelectionResiduesInfo(MolName, ChainIDs, "NonStandardAminoAcids")
     257
     258 def ListLigandsInfo(MolName, ChainIDs):
     259     """List ligand information across all chains."""
     260
     261     if not OptionsInfo["Ligands"]:
     262         return
     263
     264     ListSelectionResiduesInfo(MolName, ChainIDs, "Ligands")
     265
     266 def ListSolventsInfo(MolName, ChainIDs):
     267     """List solvents information across all chains."""
     268
     269     if not OptionsInfo["Solvents"]:
     270         return
     271
     272     ListSelectionResiduesInfo(MolName, ChainIDs, "Solvents")
     273
     274 def ListInorganicsInfo(MolName, ChainIDs):
     275     """List inorganics information across all chains."""
     276
     277     if not OptionsInfo["Inorganics"]:
     278         return
     279
     280     ListSelectionResiduesInfo(MolName, ChainIDs, "Inorganics")
     281
     282 def ListSelectionResiduesInfo(MolName, ChainIDs, SelectionType):
     283     """List residues information for a specified selection type. """
     284
     285     Lines = []
     286     TotalResCount = 0
     287
     288     for ChainID in ChainIDs:
     289         SelectionInfo, SelectionLabel = GetSelectionResiduesInfo(MolName, ChainID, SelectionType)
     290
     291         ChainResCount = 0
     292         LineWords = []
     293
     294         SortedResNames = sorted(SelectionInfo["ResNames"], key = lambda ResName: SelectionInfo["ResCount"][ResName], reverse = True)
     295         for ResName in SortedResNames:
     296             ResCount = SelectionInfo["ResCount"][ResName]
     297             LineWords.append("%s - %s" % (ResName, ResCount))
     298
     299             ChainResCount += ResCount
     300             TotalResCount += ResCount
     301
     302         Line = "; ".join(LineWords) if len(LineWords) else None
     303         Lines.append("Chain ID: %s; Count: %s; Names:  %s" % (ChainID, ChainResCount, Line))
     304
     305     MiscUtil.PrintInfo("\nNumber of %s residues: %s" % (SelectionLabel, TotalResCount))
     306     for Line in Lines:
     307         MiscUtil.PrintInfo("%s" % Line)
     308
     309 def GetSelectionResiduesInfo(MolName, ChainID, SelectionType):
     310     """Get residues info for a specified selection type. """
     311
     312     SelectionInfo = None
     313     SelectionLabel = None
     314
     315     if re.match("^Ligands$", SelectionType, re.I):
     316         SelectionLabel = "ligand"
     317         SelectionInfo = PyMOLUtil.GetLigandResiduesInfo(MolName, ChainID)
     318     elif re.match("^Solvents$", SelectionType, re.I):
     319         SelectionLabel = "solvent"
     320         SelectionInfo = PyMOLUtil.GetSolventResiduesInfo(MolName, ChainID)
     321     elif re.match("^Inorganics$", SelectionType, re.I):
     322         SelectionLabel = "inorganic"
     323         SelectionInfo = PyMOLUtil.GetInorganicResiduesInfo(MolName, ChainID)
     324     elif re.match("^Chains$", SelectionType, re.I):
     325         SelectionLabel = "polymer chain"
     326         SelectionInfo = PyMOLUtil.GetPolymerResiduesInfo(MolName, ChainID)
     327     elif re.match("^NonStandardAminoAcids$", SelectionType, re.I):
     328         SelectionLabel = "non-standard amino acids"
     329         SelectionInfo = PyMOLUtil.GetAminoAcidResiduesInfo(MolName, ChainID, "NonStandard")
     330     else:
     331         MiscUtil.PrintError("Failed to retrieve residues information: Selection type %s is not valid..." % SelectionType)
     332
     333     return SelectionInfo, SelectionLabel
     334
     335 def ListPocketsInfo(MolName, ChainIDs):
     336     """List pockect residues information across all chains."""
     337
     338     if not (OptionsInfo["PocketLigands"] or OptionsInfo["PocketSolvents"] or OptionsInfo["PocketInorganics"]) :
     339         return
     340
     341     for ChainID in ChainIDs:
     342         MiscUtil.PrintInfo("\nListing ligand pockets information for chain %s..." % (ChainID))
     343
     344         LigandsInfo = PyMOLUtil.GetLigandResiduesInfo(MolName, ChainID)
     345         if not len(LigandsInfo["ResNames"]):
     346             MiscUtil.PrintInfo("\nNumber of residues in ligand pocket: None")
     347             MiscUtil.PrintInfo("Chain ID: %s; Ligands: None" % (ChainID))
     348             continue
     349
     350         for LigandResName in sorted(LigandsInfo["ResNames"]):
     351             for LigandResNum in LigandsInfo["ResNum"][LigandResName]:
     352                 ListPocketsPolymerInfo(MolName, ChainID, LigandResName, LigandResNum)
     353                 ListPocketsSolventsInfo(MolName, ChainID, LigandResName, LigandResNum)
     354                 ListPocketsInorganicsInfo(MolName, ChainID, LigandResName, LigandResNum)
     355
     356 def ListPocketsPolymerInfo(MolName, ChainID, LigandResName, LigandResNum):
     357     """List pockect residues information across all chains."""
     358
     359     if not OptionsInfo["PocketLigands"]:
     360         return
     361
     362     ListPocketSelectionResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, "Pockets")
     363
     364 def ListPocketsSolventsInfo(MolName, ChainID, LigandResName, LigandResNum):
     365     """List pockect solvent residues information across all chains."""
     366
     367     if not OptionsInfo["PocketSolvents"]:
     368         return
     369
     370     ListPocketSelectionResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, "PocketSolvents")
     371
     372 def ListPocketsInorganicsInfo(MolName, ChainID, LigandResName, LigandResNum):
     373     """List pockect inorganic residues information across all chains."""
     374
     375     if not OptionsInfo["PocketInorganics"]:
     376         return
     377
     378     ListPocketSelectionResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, "PocketInorganics")
     379
     380 def ListPocketSelectionResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, SelectionType):
     381     """List residues information for a specified pocket selection type. """
     382
     383     PocketDistanceCutoff  = OptionsInfo["PocketDistanceCutoff"]
     384     SelectionLabel = GetPocketSelectionResiduesInfoLabel(SelectionType)
     385
     386     SelectionInfo = GetPocketSelectionResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, PocketDistanceCutoff, SelectionType)
     387
     388     # Setup distribution of residues in the pocket...
     389     LineWords = []
     390     PocketResCount = 0
     391     SortedResNames = sorted(SelectionInfo["ResNames"], key = lambda ResName: SelectionInfo["ResCount"][ResName], reverse = True)
     392     for ResName in SortedResNames:
     393         ResCount = SelectionInfo["ResCount"][ResName]
     394         LineWords.append("%s - %s" % (ResName, ResCount))
     395         PocketResCount += ResCount
     396
     397     ResidueDistribution = "; ".join(LineWords) if len(LineWords) else None
     398
     399     # Setup residue IDs sorted by residue numbers...
     400     ResNumMap = {}
     401     for ResName in SelectionInfo["ResNames"]:
     402         for ResNum in SelectionInfo["ResNum"][ResName]:
     403             ResNumMap[ResNum] = ResName
     404
     405     LineWords = []
     406     for ResNum in sorted(ResNumMap, key = int):
     407         ResName = ResNumMap[ResNum]
     408         ResID = "%s_%s" % (ResName, ResNum)
     409         LineWords.append(ResID)
     410     ResidueIDs = ", ".join(LineWords) if len(LineWords) else None
     411
     412     MiscUtil.PrintInfo("\nNumber of %s residues in ligand pocket: %s" % (SelectionLabel, PocketResCount))
     413     MiscUtil.PrintInfo("Chain ID: %s; Ligand ID: %s_%s\nResidue distribution: %s\nResidue IDs: %s" % (ChainID, LigandResName, LigandResNum, ResidueDistribution, ResidueIDs))
     414
     415 def GetPocketSelectionResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, PocketDistanceCutoff, SelectionType):
     416     """Get pocket residues info for a specified selection type. """
     417
     418     SelectionInfo = None
     419
     420     if re.match("^Pockets$", SelectionType, re.I):
     421         SelectionInfo = PyMOLUtil.GetPocketPolymerResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, PocketDistanceCutoff)
     422     elif re.match("^PocketSolvents$", SelectionType, re.I):
     423         SelectionInfo = PyMOLUtil.GetPocketSolventResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, PocketDistanceCutoff)
     424     elif re.match("^PocketInorganics$", SelectionType, re.I):
     425         SelectionInfo = PyMOLUtil.GetPocketInorganicResiduesInfo(MolName, ChainID, LigandResName, LigandResNum, PocketDistanceCutoff)
     426     else:
     427         MiscUtil.PrintError("Failed to retrieve pocket residues information: Selection type %s is not valid..." % SelectionType)
     428
     429     return SelectionInfo
     430
     431 def ListBoundingBoxInfo(MolName):
     432     """List bounding box information. """
     433
     434     if not OptionsInfo["BoundingBox"]:
     435         return
     436
     437     MolSelection = "(%s)" % MolName
     438     MolExtents = pymol.cmd.get_extent(MolSelection)
     439
     440     XMin, YMin, ZMin = MolExtents[0]
     441     XMax, YMax, ZMax = MolExtents[1]
     442
     443     XSize = abs(XMax - XMin)
     444     YSize = abs(YMax - YMin)
     445     ZSize = abs(ZMax - ZMin)
     446
     447     MiscUtil.PrintInfo("\nBounding box coordinates: <XMin, XMax> - <%.3f, %.3f>; <YMin, YMax> - <%.3f, %.3f>; <ZMin, ZMax> - <%.3f, %.3f>" % (XMin, XMax, YMin, YMax, ZMin, ZMax))
     448     MiscUtil.PrintInfo("Bounding box size in angstroms: XSize - %.3f; YSize - %.3f; ZSize - %.3f" % (XSize, YSize, ZSize))
     449
     450 def ListFileSizeAndModificationInfo(Infile):
     451     """List file size and modification time info."""
     452
     453     MiscUtil.PrintInfo("\nFile size: %s" % MiscUtil.GetFormattedFileSize(Infile))
     454     MiscUtil.PrintInfo("Last modified: %s" % time.ctime(os.path.getmtime(Infile)))
     455     MiscUtil.PrintInfo("Created: %s" % time.ctime(os.path.getctime(Infile)))
     456
     457 def GetPocketSelectionResiduesInfoLabel(SelectionType):
     458     """Get pocket residues info label for a specified selection type. """
     459
     460     SelectionLabel = None
     461
     462     if re.match("^Pockets$", SelectionType, re.I):
     463         SelectionLabel = "polymer"
     464     elif re.match("^PocketSolvents$", SelectionType, re.I):
     465         SelectionLabel = "solvent"
     466     elif re.match("^PocketInorganics$", SelectionType, re.I):
     467         SelectionLabel = "inorganic"
     468     else:
     469         MiscUtil.PrintError("Failed to retrieve pocket residues label information: Selection type %s is not valid..." % SelectionType)
     470
     471     return SelectionLabel
     472
     473 def ProcessOptions():
     474     """Process and validate command line arguments and options"""
     475
     476     MiscUtil.PrintInfo("Processing options...")
     477
     478     # Validate options...
     479     ValidateOptions()
     480
     481     OptionsInfo["All"] = Options["--all"]
     482     OptionsInfo["BoundingBox"] = Options["--boundingBox"]
     483
     484     OptionsInfo["Chains"] = Options["--chains"]
     485
     486     OptionsInfo["CountResidues"] = Options["--countResidues"]
     487     OptionsInfo["Header"] = Options["--header"]
     488
     489     OptionsInfo["Infiles"] = Options["--infiles"]
     490     OptionsInfo["InfilesNames"] =  Options["--infilesNames"]
     491
     492     OptionsInfo["Inorganics"] = Options["--inorganics"]
     493     OptionsInfo["Ligands"] = Options["--ligands"]
     494
     495     OptionsInfo["PocketLigands"] = Options["--pocketLigands"]
     496     OptionsInfo["PocketDistanceCutoff"] = float(Options["--pocketDistanceCutoff"])
     497     OptionsInfo["PocketSolvents"] = Options["--pocketSolvents"]
     498     OptionsInfo["PocketInorganics"] = Options["--pocketInorganics"]
     499
     500     OptionsInfo["Solvents"] = Options["--solvents"]
     501
     502     # Update option values to list either default information or all information...
     503     OptionNames = ["Chains", "Header", "Ligands"]
     504     if OptionsInfo["All"]:
     505         OptionNames = ["BoundingBox", "Chains",  "CountResidues", "Header", "Inorganics", "Ligands",  "PocketSolvents", "PocketInorganics", "Solvents"]
     506
     507     for Name in OptionNames:
     508         if Name in OptionsInfo:
     509             OptionsInfo[Name] = True
     510         else:
     511             MiscUtil.PrintError("Option name %s is not a valid name..." % s)
     512
     513 def RetrieveOptions():
     514     """Retrieve command line arguments and options"""
     515
     516     # Get options...
     517     global Options
     518     Options = docopt(_docoptUsage_)
     519
     520     # Set current working directory to the specified directory...
     521     WorkingDir = Options["--workingdir"]
     522     if WorkingDir:
     523         os.chdir(WorkingDir)
     524
     525     # Handle examples option...
     526     if "--examples" in Options and Options["--examples"]:
     527         MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_))
     528         sys.exit(0)
     529
     530 def ValidateOptions():
     531     """Validate option values"""
     532
     533     # Expand infile names..
     534     InfilesNames = MiscUtil.ExpandFileNames(Options["--infiles"], ",")
     535
     536     # Validate file extensions...
     537     for Infile in InfilesNames:
     538         MiscUtil.ValidateOptionFilePath("-i, --infiles", Infile)
     539         MiscUtil.ValidateOptionFileExt("-i, --infiles", Infile, "pdb cif")
     540     Options["--infilesNames"] = InfilesNames
     541
     542     MiscUtil.ValidateOptionFloatValue("--pocketDistanceCutoff", Options["--pocketDistanceCutoff"], {">": 0.0})
     543
     544 # Setup a usage string for docopt...
     545 _docoptUsage_ = """
     546 PyMOLInfoMacromolecules.py - List information about macromolecules
     547
     548 Usage:
     549     PyMOLInfoMacromolecules.py [--all] [--boundingBox] [--chains] [--countResidues]
     550                                [--header] [--inorganics] [--ligands] [--pocketLigands]
     551                                [--pocketDistanceCutoff  <number>] [--pocketSolvents] [--pocketInorganics]
     552                                [--solvents] [-w <dir>] -i <infile1,infile2,infile3...>
     553     PyMOLInfoMacromolecules.py -h | --help | -e | --examples
     554
     555 Description:
     556     List information regarding  ID, classification, experimental technique, chains,
     557     solvents, inorganics, ligands, and ligand binding pockets in macromolecules
     558     present including proteins and nucleic acids.
     559
     560     The supported input  file format are: PDB (.pdb), mmCIF (.cif)
     561
     562 Options:
     563     -a, --all
     564         All available information.
     565     -b, --boundingBox
     566         Min and max coordinates for bounding box along with its size.
     567     -c, --chains
     568         Number of chains and their IDs. This is also default behavior.
     569      --countResidues
     570         Number of residues across chains. The chain residues are identified
     571         using polymer selection operator available in PyMOL. In addition,
     572         the non-standard amino acid residues are listed.
     573     -e, --examples
     574         Print examples.
     575     -h, --help
     576         Print this help message.
     577      --header
     578         Header information including experimental technique information
     579         along with any available resolution. This is also default behavior.
     580     -i, --infiles <infile1,infile2,infile3...>
     581         A comma delimited list of input files. The wildcards are also allowed
     582         in file names.
     583     --inorganics
     584         Inorganic residues across chains. The inorganic residues are identified
     585         using inorganic selection operator available in PyMOL.
     586     -l, --ligands
     587         Ligands across chains. This is also default behavior. The ligands
     588         residues are identified using organic selection operator available
     589         in PyMOL.
     590     -p, --pocketLigands
     591         Chain residues in ligand pockets.
     592     --pocketDistanceCutoff <number>  [default: 5.0]
     593         Distance in Angstroms for identifying pocket residues around ligands.
     594     --pocketSolvents
     595         Solvent residues in ligand pockets. The solvent residues are identified
     596         using solvent selection operator available in PyMOL.
     597     --pocketInorganics
     598         Inorganic residues in ligand pockets. The inorganic residues are identified
     599         using Inorganic selection operator available in PyMOL.
     600     -s, --solvents
     601         Solvent residues across chains. The solvent residues are identified
     602         using solvent selection operator available in PyMOL.
     603     -w, --workingdir <dir>
     604         Location of working directory which defaults to the current directory.
     605
     606 Examples:
     607     To list header, chains, and ligand information for macromolecules in input
     608     file, type:
     609
     610         % PyMOLInfoMacromolecules.py  -i Sample3.pdb
     611
     612     To list all available information for macromolecules in input files, type:
     613
     614         % PyMOLInfoMacromolecules.py  -a  -i "Sample3.pdb,Sample4.pdb"
     615
     616     To list pockets residues information along with other default information
     617     for marcomolecules in input file, type:
     618
     619         % PyMOLInfoMacromolecules.py  -p --pocketDistanceCutoff 4.5
     620         --pocketSolvents  --pocketInorganics -i Sample3.pdb
     621
     622     To list chain residues information along with other default information
     623     for marcomolecules in input file, type:
     624
     625         % PyMOLInfoMacromolecules.py  -c --countResidues --solvents
     626         --inorganics -i "Sample3.pdb,Sample4.pdb"
     627
     628 Author:
     629     Manish Sud(msud@san.rr.com)
     630
     631 See also:
     632     DownloadPDBFiles.pl, PyMOLSplitChainsAndLigands.py,
     633     PyMOLVisualizeMacromolecules.py
     634
     635 Copyright:
     636     Copyright (C) 2018 Manish Sud. All rights reserved.
     637
     638     The functionality available in this script is implemented using PyMOL, a
     639     molecular visualization system on an open source foundation originally
     640     developed by Warren DeLano.
     641
     642     This file is part of MayaChemTools.
     643
     644     MayaChemTools is free software; you can redistribute it and/or modify it under
     645     the terms of the GNU Lesser General Public License as published by the Free
     646     Software Foundation; either version 3 of the License, or (at your option) any
     647     later version.
     648
     649 """
     650
     651 if __name__ == "__main__":
     652     main()