MayaChemTools

   1 #!/bin/env python
   2 #
   3 # File: PyMOLCalculateRMSD.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     CalculateRMSDValues()
  84 
  85     MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName)
  86     MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime))
  87 
  88 
  89 def CalculateRMSDValues():
  90     """Calculate RMSD between reference and probe files."""
  91 
  92     Outfile = OptionsInfo["Outfile"]
  93     OutDelim = OptionsInfo["OutDelim"]
  94 
  95     MiscUtil.PrintInfo("\nGenerating file %s..." % Outfile)
  96     OutFH = open(Outfile, "w")
  97     if OutFH is None:
  98         MiscUtil.PrintError("Couldn't open output file: %s.\n" % (Outfile))
  99 
 100     WriteColumnLabels(OutFH, OutDelim)
 101 
 102     pymol.cmd.reinitialize()
 103     if re.match("^OneToOne$", OptionsInfo["Mode"], re.I):
 104         CalculateOneToOneRMSDValues(OutFH, OutDelim)
 105     elif re.match("^AllToAll$", OptionsInfo["Mode"], re.I):
 106         CalculateAllToAllRMSDValues(OutFH, OutDelim)
 107     elif re.match("^FirstToAll$", OptionsInfo["Mode"], re.I):
 108         CalculateFirstToAllRMSDValues(OutFH, OutDelim)
 109     else:
 110         MiscUtil.PrintError("RMSD couldn't be calculated: Specified mode, %s, is not supported" % OptionsInfo["Mode"])
 111 
 112     OutFH.close()
 113 
 114 
 115 def CalculateOneToOneRMSDValues(OutFH, OutDelim):
 116     """Calculate pairwise RMSD values."""
 117 
 118     RefFilesCount = len(OptionsInfo["RefFilesNames"])
 119     ProbeFilesCount = len(OptionsInfo["ProbeFilesNames"])
 120 
 121     FilesCount = ProbeFilesCount if RefFilesCount > ProbeFilesCount else RefFilesCount
 122 
 123     if RefFilesCount != ProbeFilesCount:
 124         MiscUtil.PrintWarning(
 125             "Number of reference files, %d,  is not equal to number of probe files, %d .\n"
 126             % (RefFilesCount, ProbeFilesCount)
 127         )
 128         MiscUtil.PrintWarning("Pairwise RMSD will be calculated only for first %s files.\n" % (FilesCount))
 129 
 130     # Process files...
 131     for FileIndex in range(0, FilesCount):
 132         RefFileIndex = FileIndex
 133         ProbeFileIndex = FileIndex
 134 
 135         LoadRefFile(RefFileIndex)
 136         LoadProbeFile(ProbeFileIndex)
 137 
 138         RMSD = CalculateRMSDValue(RefFileIndex, ProbeFileIndex)
 139 
 140         RefID = OptionsInfo["RefFilesInfo"]["FilesRoots"][RefFileIndex]
 141         ProbeID = OptionsInfo["ProbeFilesInfo"]["FilesRoots"][ProbeFileIndex]
 142         Line = "%s%s%s%s%s\n" % (RefID, OutDelim, ProbeID, OutDelim, RMSD)
 143         OutFH.write(Line)
 144 
 145         DeleteRefObject(RefFileIndex)
 146         DeleteProbeObject(ProbeFileIndex)
 147 
 148 
 149 def CalculateAllToAllRMSDValues(OutFH, OutDelim):
 150     """Calculate RMSD values between all pairs of files."""
 151 
 152     RefFilesCount = len(OptionsInfo["RefFilesNames"])
 153     ProbeFilesCount = len(OptionsInfo["ProbeFilesNames"])
 154     OutMatrix = OptionsInfo["OutMatrix"]
 155 
 156     for RefFileIndex in range(0, RefFilesCount):
 157         LoadRefFile(RefFileIndex)
 158         RefID = OptionsInfo["RefFilesInfo"]["FilesRoots"][RefFileIndex]
 159 
 160         LineWords = []
 161         if OutMatrix:
 162             LineWords.append(RefID)
 163 
 164         for ProbeFileIndex in range(0, ProbeFilesCount):
 165             LoadProbeFile(ProbeFileIndex)
 166             RMSD = CalculateRMSDValue(RefFileIndex, ProbeFileIndex)
 167             DeleteProbeObject(ProbeFileIndex)
 168 
 169             if OutMatrix:
 170                 LineWords.append(RMSD)
 171             else:
 172                 ProbeID = OptionsInfo["ProbeFilesInfo"]["FilesRoots"][ProbeFileIndex]
 173                 Line = "%s%s%s%s%s\n" % (RefID, OutDelim, ProbeID, OutDelim, RMSD)
 174                 OutFH.write(Line)
 175 
 176         DeleteRefObject(RefFileIndex)
 177 
 178         if OutMatrix:
 179             Line = OutDelim.join(LineWords)
 180             OutFH.write("%s\n" % Line)
 181 
 182 
 183 def CalculateFirstToAllRMSDValues(OutFH, OutDelim):
 184     """Calculate RMSD values between first reference file and all probe files."""
 185 
 186     # Setup reference...
 187     RefFileIndex = 0
 188     RefID = OptionsInfo["RefFilesInfo"]["FilesRoots"][RefFileIndex]
 189     LoadRefFile(RefFileIndex)
 190 
 191     # Go over probe files...
 192     for ProbeFileIndex in range(0, len(OptionsInfo["ProbeFilesNames"])):
 193         LoadProbeFile(ProbeFileIndex)
 194 
 195         RMSD = CalculateRMSDValue(RefFileIndex, ProbeFileIndex)
 196 
 197         ProbeID = OptionsInfo["ProbeFilesInfo"]["FilesRoots"][ProbeFileIndex]
 198         Line = "%s%s%s%s%s\n" % (RefID, OutDelim, ProbeID, OutDelim, RMSD)
 199         OutFH.write(Line)
 200 
 201         DeleteProbeObject(ProbeFileIndex)
 202 
 203     DeleteRefObject(RefFileIndex)
 204 
 205 
 206 def WriteColumnLabels(OutFH, OutDelim):
 207     """Write out column labels."""
 208 
 209     ColLabels = []
 210 
 211     if re.match("^AllToAll$", OptionsInfo["Mode"], re.I) and OptionsInfo["OutMatrix"]:
 212         ColLabels.append("")
 213         ColLabels.extend(OptionsInfo["ProbeFilesInfo"]["FilesRoots"])
 214     else:
 215         ColLabels = ["RefFileID", "ProbeFileID", "RMSD"]
 216 
 217     Line = OutDelim.join(ColLabels)
 218     OutFH.write("%s\n" % Line)
 219 
 220 
 221 def LoadRefFile(RefFileIndex):
 222     """Load reference file."""
 223 
 224     RefFile = OptionsInfo["RefFilesNames"][RefFileIndex]
 225     RefName = OptionsInfo["RefFilesInfo"]["PyMOLObjectNames"][RefFileIndex]
 226     LoadFile(RefFile, RefName)
 227 
 228 
 229 def LoadProbeFile(ProbeFileIndex):
 230     """Load probe file."""
 231 
 232     ProbeFile = OptionsInfo["ProbeFilesNames"][ProbeFileIndex]
 233     ProbeName = OptionsInfo["ProbeFilesInfo"]["PyMOLObjectNames"][ProbeFileIndex]
 234     LoadFile(ProbeFile, ProbeName)
 235 
 236 
 237 def LoadFile(FileName, ObjectName):
 238     """Load a file."""
 239 
 240     pymol.cmd.load(FileName, ObjectName)
 241 
 242 
 243 def DeleteRefObject(RefFileIndex):
 244     """Delete reference object."""
 245 
 246     RefName = OptionsInfo["RefFilesInfo"]["PyMOLObjectNames"][RefFileIndex]
 247     DeleteObject(RefName)
 248 
 249 
 250 def DeleteProbeObject(ProbeFileIndex):
 251     """Delete probe object."""
 252 
 253     ProbeName = OptionsInfo["ProbeFilesInfo"]["PyMOLObjectNames"][ProbeFileIndex]
 254     DeleteObject(ProbeName)
 255 
 256 
 257 def DeleteObject(Name):
 258     """Delete PyMOL object."""
 259 
 260     pymol.cmd.delete(Name)
 261 
 262 
 263 def CalculateRMSDValue(RefFileIndex, ProbeFileIndex):
 264     """Calculate RMSD value between referece and probe objects."""
 265 
 266     RefName = OptionsInfo["RefFilesInfo"]["PyMOLObjectNames"][RefFileIndex]
 267     ProbeName = OptionsInfo["ProbeFilesInfo"]["PyMOLObjectNames"][ProbeFileIndex]
 268 
 269     if re.match("^FirstChain$", OptionsInfo["AlignMode"], re.I):
 270         RefFirstChainID = OptionsInfo["RefFilesInfo"]["ChainIDs"][RefFileIndex][0]
 271         RefSelection = "(%s and chain %s)" % (RefName, RefFirstChainID)
 272 
 273         ProbeFirstChainID = OptionsInfo["ProbeFilesInfo"]["ChainIDs"][ProbeFileIndex][0]
 274         ProbeSelection = "(%s and chain %s)" % (ProbeName, ProbeFirstChainID)
 275     else:
 276         RefSelection = RefName
 277         ProbeSelection = ProbeName
 278 
 279     RMSD = CalculateRMSD(RefSelection, ProbeSelection, OptionsInfo["AlignMethod"])
 280 
 281     return RMSD
 282 
 283 
 284 def CalculateRMSD(RefSelectionName, ProbeSelectionName, AlignMethod):
 285     """Calculate RMSD between two selections after aligning the selections."""
 286 
 287     if re.match("^align$", AlignMethod, re.I):
 288         Results = pymol.cmd.align(ProbeSelectionName, RefSelectionName)
 289         RMSD = Results[0]
 290     elif re.match("^cealign$", AlignMethod, re.I):
 291         Results = pymol.cmd.cealign(RefSelectionName, ProbeSelectionName)
 292         RMSD = Results["RMSD"]
 293     elif re.match("^super$", AlignMethod, re.I):
 294         Results = pymol.cmd.super(ProbeSelectionName, RefSelectionName)
 295         RMSD = Results[0]
 296     else:
 297         RMSD = None
 298         MiscUtil.PrintWarning("Failed to calculate RMSD. Unknown alignment method: %s" % AlignMethod)
 299 
 300     if RMSD is not None:
 301         RMSD = "%.2f" % RMSD
 302 
 303     return RMSD
 304 
 305 
 306 def RetrieveProbeFilesInfo():
 307     """Retrieve information for probe input files."""
 308 
 309     RetrieveInfilesInfo("ProbeFiles")
 310 
 311 
 312 def RetrieveRefFilesInfo():
 313     """Retrieve information for reference input files."""
 314 
 315     RetrieveInfilesInfo("RefFiles")
 316 
 317 
 318 def RetrieveInfilesInfo(InfilesMode):
 319     """Retrieve information for input files."""
 320 
 321     if re.match("^ProbeFiles$", InfilesMode, re.I):
 322         MiscUtil.PrintInfo("Retrieving information for probe files...")
 323         InfilesNames = OptionsInfo["ProbeFilesNames"]
 324         NameSuffix = "_Probe"
 325     elif re.match("^RefFiles$", InfilesMode, re.I):
 326         MiscUtil.PrintInfo("Retrieving information for reference files...")
 327         InfilesNames = OptionsInfo["RefFilesNames"]
 328         NameSuffix = "_Ref"
 329     else:
 330         MiscUtil.PrintError("Internal Error: Unknown infiles mode: %s" % InfilesMode)
 331 
 332     InfilesInfo = {}
 333 
 334     InfilesInfo["FilesNames"] = []
 335     InfilesInfo["FilesRoots"] = []
 336     InfilesInfo["ChainIDs"] = []
 337     InfilesInfo["PyMOLObjectNames"] = []
 338 
 339     for Infile in InfilesNames:
 340         MiscUtil.PrintInfo("\nRetrieving chains information for input file %s..." % Infile)
 341 
 342         FileDir, FileName, FileExt = MiscUtil.ParseFileName(Infile)
 343         InfileRoot = FileName
 344 
 345         ChainIDs = RetrieveChainIDs(Infile, InfileRoot)
 346         if not len(ChainIDs):
 347             if re.match("^FirstChain$", OptionsInfo["AlignMode"], re.I):
 348                 MiscUtil.PrintError(
 349                     "The align mode, %s, can't be used for calculating RMSD: No non-empty chain IDs found in input file."
 350                     % (OptionsInfo["AlignMode"])
 351                 )
 352 
 353         InfilesInfo["FilesNames"].append(Infile)
 354         InfilesInfo["FilesRoots"].append(InfileRoot)
 355         InfilesInfo["ChainIDs"].append(ChainIDs)
 356 
 357         Name = "%s%s" % (InfileRoot, NameSuffix)
 358         InfilesInfo["PyMOLObjectNames"].append(Name)
 359 
 360     if re.match("^ProbeFiles$", InfilesMode, re.I):
 361         OptionsInfo["ProbeFilesInfo"] = InfilesInfo
 362     elif re.match("^RefFiles$", InfilesMode, re.I):
 363         OptionsInfo["RefFilesInfo"] = InfilesInfo
 364 
 365 
 366 def RetrieveChainIDs(Infile, InfileRoot):
 367     """Retrieve chains IDs for an input file."""
 368 
 369     pymol.cmd.reinitialize()
 370 
 371     MolName = InfileRoot
 372     pymol.cmd.load(Infile, MolName)
 373 
 374     ChainIDs = PyMOLUtil.GetChains(MolName, RemoveEmpty=True)
 375     pymol.cmd.delete(MolName)
 376 
 377     if ChainIDs is None:
 378         ChainIDs = []
 379 
 380     # Print out chain and ligand IDs...
 381     ChainInfo = ", ".join(ChainIDs) if len(ChainIDs) else "None"
 382     MiscUtil.PrintInfo("Chain IDs: %s" % ChainInfo)
 383 
 384     return ChainIDs
 385 
 386 
 387 def ProcessOptions():
 388     """Process and validate command line arguments and options."""
 389 
 390     MiscUtil.PrintInfo("Processing options...")
 391 
 392     # Validate options...
 393     ValidateOptions()
 394 
 395     OptionsInfo["AlignMethod"] = Options["--alignMethod"].lower()
 396     OptionsInfo["AlignMode"] = Options["--alignMode"]
 397 
 398     OptionsInfo["Mode"] = Options["--mode"]
 399 
 400     OptionsInfo["ProbeFiles"] = Options["--probefiles"]
 401     OptionsInfo["ProbeFilesNames"] = Options["--probeFilesNames"]
 402 
 403     OptionsInfo["RefFiles"] = Options["--reffiles"]
 404     OptionsInfo["RefFilesNames"] = Options["--refFilesNames"]
 405 
 406     RetrieveProbeFilesInfo()
 407     RetrieveRefFilesInfo()
 408 
 409     OptionsInfo["Outfile"] = Options["--outfile"]
 410     OptionsInfo["OutMatrix"] = True if re.match("^Yes$", Options["--outMatrix"], re.I) else False
 411 
 412     OptionsInfo["Overwrite"] = Options["--overwrite"]
 413 
 414     OptionsInfo["OutDelim"] = " "
 415     if MiscUtil.CheckFileExt(OptionsInfo["Outfile"], "csv"):
 416         OptionsInfo["OutDelim"] = ","
 417     elif MiscUtil.CheckFileExt(OptionsInfo["Outfile"], "tsv txt"):
 418         OptionsInfo["OutDelim"] = "\t"
 419     else:
 420         MiscUtil.PrintError(
 421             'The file name specified , %s, for option "--outfile" is not valid. Supported file formats: csv tsv txt\n'
 422             % (OptionsInfo["Outfile"])
 423         )
 424 
 425 
 426 def RetrieveOptions():
 427     """Retrieve command line arguments and options."""
 428 
 429     # Get options...
 430     global Options
 431     Options = docopt(_docoptUsage_)
 432 
 433     # Set current working directory to the specified directory...
 434     WorkingDir = Options["--workingdir"]
 435     if WorkingDir:
 436         os.chdir(WorkingDir)
 437 
 438     # Handle examples option...
 439     if "--examples" in Options and Options["--examples"]:
 440         MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_))
 441         sys.exit(0)
 442 
 443 
 444 def ValidateOptions():
 445     """Validate option values."""
 446 
 447     MiscUtil.ValidateOptionTextValue("-a, --alignMethod", Options["--alignMethod"], "align cealign super")
 448     MiscUtil.ValidateOptionTextValue("--alignMode", Options["--alignMode"], "FirstChain Complex")
 449 
 450     MiscUtil.ValidateOptionTextValue("-m, --mode", Options["--mode"], "OneToOne AllToAll FirstToAll")
 451 
 452     # Expand reffiles to handle presence of multiple input files...
 453     RefFilesNames = MiscUtil.ExpandFileNames(Options["--reffiles"], ",")
 454 
 455     # Validate file extensions...
 456     for RefFile in RefFilesNames:
 457         MiscUtil.ValidateOptionFilePath("-r, --reffiles", RefFile)
 458         MiscUtil.ValidateOptionFileExt("-r, --reffiles", RefFile, "pdb cif")
 459     Options["--refFilesNames"] = RefFilesNames
 460 
 461     # Expand probefiles to handle presence of multiple input files...
 462     ProbeFilesNames = MiscUtil.ExpandFileNames(Options["--probefiles"], ",")
 463 
 464     # Validate file extensions...
 465     for ProbeFile in ProbeFilesNames:
 466         MiscUtil.ValidateOptionFilePath("-p, --probefiles", ProbeFile)
 467         MiscUtil.ValidateOptionFileExt("-p, --probefiles", ProbeFile, "pdb cif")
 468     Options["--probeFilesNames"] = ProbeFilesNames
 469 
 470     MiscUtil.ValidateOptionFileExt("-o, --outfile", Options["--outfile"], "csv tsv txt")
 471     MiscUtil.ValidateOptionsOutputFileOverwrite(
 472         "-o, --outfile", Options["--outfile"], "--overwrite", Options["--overwrite"]
 473     )
 474 
 475     MiscUtil.ValidateOptionTextValue("--outMatrix", Options["--outMatrix"], "Yes No")
 476 
 477 
 478 # Setup a usage string for docopt...
 479 _docoptUsage_ = """
 480 PyMOLCalculateRMSD.py - Calculate RMSD between macromolecules
 481 
 482 Usage:
 483     PyMOLCalculateRMSD.py [--alignMethod <align, cealign, super>]
 484                           [--alignMode <FirstChain or Complex>] [--mode <OneToOne, AllToAll, FirstToAll>]
 485                           [--outMatrix <yes or no>] [--overwrite]
 486                           [-w <dir>] -p <probefile1,probefile2,probefile3...> -r <reffile1,reffile2,reffile3...> -o <outfile>
 487     PyMOLCalculateRMSD.py -h | --help | -e | --examples
 488 
 489 Description:
 490     Calculate Root Mean Square Distance (RMSD) between a set of similar
 491     macromolecules in reference and probe input files. The probe and reference
 492     files are spatially aligned before the the calculation of RMSD values.
 493 
 494     The supported input  file format are: PDB (.pdb), mmCIF (.cif)
 495 
 496     The supported output file formats are:  CSV (.csv), TSV (.tsv, .txt)
 497 
 498 Options:
 499     -a, --alignMethod <align, cealign, super>  [default: super]
 500         Alignment methodology to use for aligning probe input files to
 501         reference files.
 502     --alignMode <FirstChain or Complex>  [default: FirstChain]
 503         Portion of probe and reference files to use for spatial alignment of
 504         probe files against reference files.  Possible values: FirstChain or
 505         Complex.
 506         
 507         The FirstChain mode allows alignment of the first chain in probe files
 508         to the first chain in reference files along with moving the rest of the
 509         complex to coordinate space of the reference files. The complete
 510         complex in probe files is aligned to the complete complex in reference
 511         files for the Complex mode.
 512     -e, --examples
 513         Print examples.
 514     -h, --help
 515         Print this help message.
 516     -m, --mode <OneToOne, AllToAll, FirstToAll>  [default: OneToOne]
 517         Specify how reference and probe input files are handled during the calculation
 518         of RMSD between reference and probe files.  Possible values: OneToOne,
 519         AllToAll and AllToFirst. For OneToOne mode, the number of reference input
 520         files must be equal to the number of probe input files. The RMSD is
 521         calculated for each pair of reference and probe file and written to the
 522         output file. For AllToAll mode, the RMSD is calculated for each reference
 523         input file against all probe input files. For FirstToAll mode, however, the RMSD
 524         is only calculated for the first reference input file against all probe files.
 525     -p, --probefiles <probefile1,probefile2,probelfile3...>
 526         A comma delimited list of probe input files. The wildcards are also allowed
 527         in file names.
 528     -r, --reffiles <reffile1,reffile2,reffile3...>
 529         A comma delimited list of reference input files. The wildcards are also allowed
 530         in file names.
 531     -o, --outfile <outfile>
 532         Output file name for writing out RMSD values. Supported text file extensions:
 533         csv, tsv or txt.
 534     --outMatrix <yes or no>  [default: yes]
 535         Output file in a matrix format during 'AllToAll' value for '-m, --mode' option.
 536     --overwrite
 537         Overwrite existing files.
 538     -w, --workingdir <dir>
 539         Location of working directory which defaults to the current directory.
 540 
 541 Examples:
 542     To calculate RMSD between pair of macromolecules in reference and probe files
 543     using only first chain in each file and write out a CSV file containing calculated RMSD
 544     values along with IDs, type:
 545 
 546         % PyMOLCalculateRMSD.py  -r "Sample3.pdb,Sample4.pdb,Sample5.pdb"
 547           -p "Sample3.pdb,Sample4.pdb,Sample5.pdb" -o SampleOut.csv 
 548 
 549     To calculate RMSD between all macromolecules in reference and probe files using
 550     complete complex and write out a CSV matrix file, type:
 551 
 552         % PyMOLCalculateRMSD.py  -m AllToAll --alignMode Complex
 553            --outMatrix Yes -r "Sample3.pdb,Sample4.pdb,Sample5.pdb"
 554           -p "Sample3.pdb,Sample4.pdb" -o SampleOut.csv 
 555 
 556     To calculate RMSD between macromolecule in first reference against all probe files
 557     using only first chain in each file and write out a TSV file containing calculated RMSD
 558     values along with IDs, type:
 559 
 560         % PyMOLCalculateRMSD.py  -m FirstToAll
 561           -r "Sample3.pdb,Sample4.pdb,Sample5.pdb"
 562           -p "Sample3.pdb,Sample4.pdb,Sample5.pdb" -o SampleOut.tsv 
 563 
 564     To calculate RMSD between pair of macromolecules in reference and probe files
 565     using only first chain in each file along with a specific alignment method and write
 566     out a CSV file containing calculated RMSD values, type:
 567 
 568         % PyMOLCalculateRMSD.py  --alignMethod align
 569           -r "Sample3.pdb,Sample4.pdb,Sample5.pdb"
 570           -p "Sample3.pdb,Sample4.pdb,Sample5.pdb" -o SampleOut.csv 
 571 
 572 Author:
 573     Manish Sud(msud@san.rr.com)
 574 
 575 See also:
 576     PyMOLAlignChains.py, PyMOLSplitChainsAndLigands.py,
 577     PyMOLVisualizeMacromolecules.py
 578 
 579 Copyright:
 580     Copyright (C) 2026 Manish Sud. All rights reserved.
 581 
 582     The functionality available in this script is implemented using PyMOL, a
 583     molecular visualization system on an open source foundation originally
 584     developed by Warren DeLano.
 585 
 586     This file is part of MayaChemTools.
 587 
 588     MayaChemTools is free software; you can redistribute it and/or modify it under
 589     the terms of the GNU Lesser General Public License as published by the Free
 590     Software Foundation; either version 3 of the License, or (at your option) any
 591     later version.
 592 
 593 """
 594 
 595 if __name__ == "__main__":
 596     main()