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