MayaChemTools

    1 #!/bin/env python
    2 #
    3 # File: PyMOLGenerateRamachandranPlots.py
    4 # Author: Manish Sud <msud@san.rr.com>
    5 #
    6 # Copyright (C) 2021 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 import csv
   38 import matplotlib.pyplot as plt
   39 import numpy as np
   40 
   41 # PyMOL imports...
   42 try:
   43     import pymol
   44     # Finish launching PyMOL in  a command line mode for batch processing (-c)
   45     # along with the following options:  disable loading of pymolrc and plugins (-k);
   46     # suppress start up messages (-q)
   47     pymol.finish_launching(['pymol', '-ckq'])
   48 except ImportError as ErrMsg:
   49     sys.stderr.write("\nFailed to import PyMOL module/package: %s\n" % ErrMsg)
   50     sys.stderr.write("Check/update your PyMOL environment and try again.\n\n")
   51     sys.exit(1)
   52 
   53 # MayaChemTools imports...
   54 try:
   55     from docopt import docopt
   56     import MiscUtil
   57     import PyMOLUtil
   58 except ImportError as ErrMsg:
   59     sys.stderr.write("\nFailed to import MayaChemTools module/package: %s\n" % ErrMsg)
   60     sys.stderr.write("Check/update your MayaChemTools environment and try again.\n\n")
   61     sys.exit(1)
   62 
   63 ScriptName = os.path.basename(sys.argv[0])
   64 Options = {}
   65 OptionsInfo = {}
   66 
   67 def main():
   68     """Start execution of the script"""
   69     
   70     MiscUtil.PrintInfo("\n%s (PyMOL v%s; %s) Starting...\n" % (ScriptName, pymol.cmd.get_version()[0], time.asctime()))
   71 
   72     (WallClockTime, ProcessorTime) = MiscUtil.GetWallClockAndProcessorTime()
   73     
   74     # Retrieve command line arguments and options...
   75     RetrieveOptions()
   76     
   77     # Process and validate command line arguments and options...
   78     ProcessOptions()
   79     
   80     # Perform actions required by the script...
   81     GenerateRamachandranPlots()
   82     
   83     MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName)
   84     MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime))
   85 
   86 def GenerateRamachandranPlots():
   87     """Calculate phi and psi angles for macromolecules containing amino acids
   88     and generate Ramachandran plots.
   89     """
   90 
   91     # Calculate phi and psi angles..
   92     CalculatePhiPsiAngles()
   93 
   94     # Read phi and psi densities...
   95     ReadPhiAndPsiDensities()
   96 
   97     # Setup contour info...
   98     SetupContoursInfo()
   99 
  100     # Generate plots...
  101     if OptionsInfo["MultipleOutFiles"]:
  102         GenerateMultiplePlotFiles()
  103     else:
  104         GenerateSinglePlotFile()
  105 
  106 def GenerateSinglePlotFile():
  107     """Generate a single plot file containg all four types of Ramachandran plots."""
  108     
  109     Outfile = OptionsInfo["Outfile"]
  110     MiscUtil.PrintInfo("\nGenerating output file %s..." % (Outfile))
  111 
  112     SetupFontFamily()
  113 
  114     # Setup figure...
  115     PlotFigure, Axes = plt.subplots(2, 2, figsize = (OptionsInfo["FigWidth"], OptionsInfo["FigHeight"]), dpi = OptionsInfo["FigDPI"])
  116     PlotAxes = [Axis for RowAxes in Axes for Axis in RowAxes]
  117 
  118     # Adjust space between subplots...
  119     plt.subplots_adjust(left  = 0.1, right = 0.9,  bottom = 0.1, top = 0.9, wspace = 0.3, hspace = 0.25)
  120 
  121     for  PlotIndex, PlotType in enumerate(OptionsInfo["PlotTypesInfo"]["Types"]):
  122         DrawPlot(PlotAxes[PlotIndex], PlotType)
  123 
  124     # Save figure...
  125     plt.savefig(Outfile)
  126 
  127 def GenerateMultiplePlotFiles():
  128     """Generate multiple plot files corresponding to four types of Ramachandran plots."""
  129     
  130     MiscUtil.PrintInfo("\nGenerating multiple output files...")
  131     
  132     SetupFontFamily()
  133 
  134     for  PlotType in OptionsInfo["PlotTypesInfo"]["Types"]:
  135         Outfile = OptionsInfo["PlotTypesInfo"]["Outfiles"][PlotType]
  136         MiscUtil.PrintInfo("Generating  output file %s..." % Outfile)
  137         
  138         PlotFigure, PlotAxis = plt.subplots(1, 1, figsize = (OptionsInfo["FigWidth"], OptionsInfo["FigHeight"]), dpi = OptionsInfo["FigDPI"])
  139         DrawPlot(PlotAxis, PlotType)
  140 
  141         # Save figure...
  142         plt.savefig(Outfile)
  143 
  144         # Get ready for the next figure...
  145         plt.clf()
  146 
  147 def DrawPlot(PlotAxis, PlotType):
  148     """Draw contour and scatter plot """
  149     
  150     PlotTypesInfo = OptionsInfo["PlotTypesInfo"]
  151     
  152     # Draw filled contours...
  153     PhiPsiContourInfo = PlotTypesInfo["PhiPsiContourInfo"][PlotType]
  154     PlotAxis.contourf(PhiPsiContourInfo["X"], PhiPsiContourInfo["Y"], PhiPsiContourInfo["Z"], levels = PlotTypesInfo["Levels"][PlotType], colors = PlotTypesInfo["Colors"][PlotType])
  155     
  156     # Draw scatter plot for phi and psi angles...
  157     if PlotTypesInfo["ResCount"][PlotType]:
  158         PlotAxis.scatter(PlotTypesInfo["PhiAngles"][PlotType], PlotTypesInfo["PsiAngles"][PlotType], s = OptionsInfo["ScatterMarkerSize"], c = OptionsInfo["ScatterMarkerColor"], marker = OptionsInfo["ScatterMarkerStyle"])
  159     
  160     # Setup limits...
  161     PlotAxis.set_xlim(PlotTypesInfo["Limits"][PlotType])
  162     PlotAxis.set_ylim(PlotTypesInfo["Limits"][PlotType])
  163     
  164     # Setup major tick marks...
  165     PlotAxis.set_xticklabels(PlotTypesInfo["MajorTickLabels"][PlotType], fontdict = {"fontsize": OptionsInfo["FontTicksSize"], "fontweight": OptionsInfo["FontTicksWeight"]})
  166     PlotAxis.set_xticks(PlotTypesInfo["MajorTickPositions"][PlotType])
  167     PlotAxis.set_yticklabels(PlotTypesInfo["MajorTickLabels"][PlotType], fontdict = {"fontsize": OptionsInfo["FontTicksSize"], "fontweight": OptionsInfo["FontTicksWeight"]})
  168     PlotAxis.set_yticks(PlotTypesInfo["MajorTickPositions"][PlotType])
  169 
  170     # Set up minor ticks...
  171     if OptionsInfo["TicksMinor"]:
  172         PlotAxis.set_xticks(PlotTypesInfo["MinorTickPositions"][PlotType], minor = True)
  173         PlotAxis.set_yticks(PlotTypesInfo["MinorTickPositions"][PlotType], minor = True)
  174 
  175     # Setup grid...
  176     if OptionsInfo["Grid"]:
  177         PlotAxis.grid(True, color = OptionsInfo["GridLineColor"],  linestyle = OptionsInfo["GridLineStyle"], linewidth = OptionsInfo["GridLineWidth"])
  178     
  179     # Setup title...
  180     PlotAxis.set_title(PlotTypesInfo["Titles"][PlotType], fontsize = OptionsInfo["FontTitleSize"], fontweight = OptionsInfo["FontTitleWeight"])
  181     
  182     # Setup axes labels...
  183     if PlotTypesInfo["DrawXLabel"][PlotType]:
  184         XLabel = r"$\Phi$" if OptionsInfo["Greek"] else "Phi"
  185         PlotAxis.set_xlabel(XLabel, fontsize = OptionsInfo["FontAxesSize"], fontweight = OptionsInfo["FontAxesWeight"])
  186     if PlotTypesInfo["DrawYLabel"][PlotType]:
  187         # Setup a horizontal ylabel close to the axis...
  188         YLabel = r"$\Psi$" if OptionsInfo["Greek"] else "Psi"
  189         YRotation = 0 if OptionsInfo["Greek"] else 90
  190         PlotAxis.set_ylabel(YLabel, fontsize = OptionsInfo["FontAxesSize"], fontweight = OptionsInfo["FontAxesWeight"], rotation = YRotation, labelpad = 0)
  191 
  192 def SetupFontFamily():
  193     """Setuo global font family. """
  194     
  195     if re.match("^auto$", OptionsInfo["FontFamily"], re.I):
  196         return
  197     plt.rcParams["font.family"] = OptionsInfo["FontFamily"]
  198 
  199 def SetupContoursInfo():
  200     """Setup contour info for generating contour plots."""
  201 
  202     MiscUtil.PrintInfo("\nProcessing phi and psi densities for contour plots...")
  203     
  204     OptionsInfo["PlotTypesInfo"]["PhiPsiContourInfo"] = {}
  205     for  PlotType in OptionsInfo["PlotTypesInfo"]["Types"]:
  206         PhiPsiContourInfo = SetupPhiAndPsiContourInfo(PlotType)
  207         OptionsInfo["PlotTypesInfo"]["PhiPsiContourInfo"][PlotType] = PhiPsiContourInfo
  208 
  209 def SetupPhiAndPsiContourInfo(PlotType):
  210     """Setup X, Y and Z contour arrays for generating contour plots. """
  211 
  212     DensityInfo = OptionsInfo["PlotTypesInfo"]["PhiPsiDensityInfo"][PlotType]
  213     
  214     X, Y = np.meshgrid(DensityInfo["PhiValues"], DensityInfo["PsiValues"])
  215     Z = np.zeros((len(DensityInfo["PhiValues"]), len(DensityInfo["PsiValues"])))
  216     
  217     # Initialize X, Y, and Z arrays for contour plots...
  218     for ZRowIndex, PsiID in enumerate(DensityInfo["PhiIDs"]):
  219         for ZColIndex, PhiID in enumerate(DensityInfo["PsiIDs"]):
  220             Z[ZRowIndex][ZColIndex] = DensityInfo["Density"][PhiID][PsiID]
  221             
  222     # Track contour data...
  223     ContourInfo = {}
  224     ContourInfo["X"] = X
  225     ContourInfo["Y"] = Y
  226     ContourInfo["Z"] = Z
  227     
  228     return ContourInfo
  229     
  230 def ReadPhiAndPsiDensities():
  231     """Read phi and psi densities for generating filled contours. """
  232 
  233     OptionsInfo["PlotTypesInfo"]["PhiPsiDensityInfo"] = {}
  234     for  PlotType in OptionsInfo["PlotTypesInfo"]["Types"]:
  235         DensityFile = OptionsInfo["PlotTypesInfo"]["PhiPsiDensityFiles"][PlotType]
  236         PhiPsiDensityInfo = ReadPhiAndPsiDensityFile(DensityFile)
  237         OptionsInfo["PlotTypesInfo"]["PhiPsiDensityInfo"][PlotType] = PhiPsiDensityInfo
  238     
  239 def ReadPhiAndPsiDensityFile(DensityFile):
  240     """Read phi and psi desnsity file.
  241 
  242     Format: 
  243     Phi,Psi,Density
  244     -179.0,-179.0,0.00782923406455425
  245     -179.0,-177.0,0.00641357067237856
  246     ... ... ...
  247     """
  248 
  249     MiscUtil.PrintInfo("\nReading psi and psi density grid file %s..." % DensityFile)
  250     
  251     DensityFH = open(DensityFile, "r")
  252     if DensityFH is None:
  253         MiscUtil.PrintError("Couldn't open phi and psi density file: %s.\n" % (DensityFile))
  254     
  255     HeaderLine = True
  256     DensityLines = []
  257     for Line in DensityFH:
  258         Line = Line.rstrip()
  259         # Ignore comments...
  260         if re.match("^#", Line, re.I):
  261             continue
  262         # Ignore header line...
  263         if HeaderLine:
  264             HeaderLine = False
  265             continue
  266         DensityLines.append(Line)
  267 
  268     DensityInfo = {}
  269     DensityInfo["Density"] = {}
  270     
  271     DensityInfo["PhiIDs"] = []
  272     DensityInfo["PhiValues"] = []
  273     
  274     DensityInfo["PsiIDs"] = []
  275     DensityInfo["PsiValues"] = []
  276     
  277     PhiValuesMap = {}
  278     PsiValuesMap = {}
  279     
  280     Count = 0
  281     MinDensity = 99999.0
  282     MaxDensity = - MinDensity
  283     
  284     DensityReader = csv.reader(DensityLines, delimiter=',', quotechar='"')
  285     for LineWords in DensityReader:
  286         Count += 1
  287         
  288         Phi = LineWords[0]
  289         Psi = LineWords[1]
  290         Density = LineWords[2]
  291 
  292         # Track unique phi and psi value...
  293         if not Phi in PhiValuesMap:
  294             PhiValuesMap[Phi] = float(Phi)
  295         if not Psi in PsiValuesMap:
  296             PsiValuesMap[Psi] = float(Psi)
  297         
  298         # Track density data...
  299         if not Phi in DensityInfo["Density"]:
  300             DensityInfo["Density"][Phi] = {}
  301 
  302         Density = float(Density)
  303         DensityInfo["Density"][Phi][Psi] = Density
  304         if Density < MinDensity:
  305             MinDensity = Density
  306         if Density > MaxDensity:
  307             MaxDensity = Density
  308 
  309     # Sort and track values for phi and psi angles...
  310     DensityInfo["PhiIDs"] = sorted(PhiValuesMap.keys(), key = lambda Phi: PhiValuesMap[Phi])
  311     DensityInfo["PhiValues"] = [PhiValuesMap[Phi] for Phi in DensityInfo["PhiIDs"]]
  312     
  313     DensityInfo["PsiIDs"] = sorted(PsiValuesMap.keys(), key = lambda Psi: PsiValuesMap[Psi])
  314     DensityInfo["PsiValues"] = [PsiValuesMap[Psi] for Psi in DensityInfo["PsiIDs"]]
  315     
  316     MiscUtil.PrintInfo("Minimum density: %.4f; Maximum density: %.4f" % (MinDensity, MaxDensity))
  317     MiscUtil.PrintInfo("Number of phi and psi angles: %s" % Count)
  318     
  319     MiscUtil.PrintInfo("\nDimensions of phi and psi grid angles:")
  320     MiscUtil.PrintInfo("Phi - Min: %s; Max: %s; Bin size: %s; Count: %s" % (DensityInfo["PhiValues"][0], DensityInfo["PhiValues"][-1], abs(DensityInfo["PhiValues"][1] - DensityInfo["PhiValues"][0]), len(DensityInfo["PhiValues"])))
  321     MiscUtil.PrintInfo("Psi - Min: %s; Max: %s; Bin size: %s; Count: %s" % (DensityInfo["PsiValues"][0], DensityInfo["PsiValues"][-1], abs(DensityInfo["PsiValues"][1] - DensityInfo["PsiValues"][0]), len(DensityInfo["PsiValues"])))
  322 
  323     return DensityInfo
  324     
  325 def CalculatePhiPsiAngles():
  326     """Calculate phi and psi angles for scatter plots."""
  327 
  328     Infile = OptionsInfo["Infile"]
  329     MolName = OptionsInfo["InfileRoot"]
  330 
  331     # Load molecule...
  332     pymol.cmd.reinitialize()
  333     pymol.cmd.load(Infile, MolName)
  334     
  335     MiscUtil.PrintInfo("\nCalculating phi and psi torsion angles for input file %s..." % Infile)
  336 
  337     # Initialize...
  338     OptionsInfo["PlotTypesInfo"]["PhiAngles"] = {}
  339     OptionsInfo["PlotTypesInfo"]["PsiAngles"] = {}
  340     OptionsInfo["PlotTypesInfo"]["ResCount"] = {}
  341     for PlotType in  OptionsInfo["PlotTypesInfo"]["Types"]:
  342         OptionsInfo["PlotTypesInfo"]["PhiAngles"][PlotType] = []
  343         OptionsInfo["PlotTypesInfo"]["PsiAngles"][PlotType] = []
  344         OptionsInfo["PlotTypesInfo"]["ResCount"][PlotType] = 0
  345 
  346     Precision = OptionsInfo["Precision"]
  347     
  348     TotalResCount = 0
  349     # Go over specified chain IDs..
  350     for ChainID in OptionsInfo["SpecifiedChainsAndLigandsInfo"]["ChainIDs"]:
  351         PhiPsiInfoList = []
  352         GeneralPhiPsiInfo, GlycinePhiPsiInfo, ProlinePhiPsiInfo, PreProlinePhiPsiInfo = PyMOLUtil.GetPhiPsiCategoriesResiduesInfo(MolName, ChainID)
  353         PhiPsiInfoList.extend([GeneralPhiPsiInfo, GlycinePhiPsiInfo, ProlinePhiPsiInfo, PreProlinePhiPsiInfo])
  354         
  355         for Index, PlotType in enumerate(OptionsInfo["PlotTypesInfo"]["Types"]):
  356             PhiPsiInfo = PhiPsiInfoList[Index]
  357             ResCount = len(PhiPsiInfo["ResNums"])
  358             if not ResCount:
  359                 continue
  360 
  361             TotalResCount += ResCount
  362             OptionsInfo["PlotTypesInfo"]["ResCount"][PlotType] += ResCount
  363             
  364             PhiAngles, PsiAngles = ProcessPsiInfo(PhiPsiInfo, Precision)
  365             OptionsInfo["PlotTypesInfo"]["PhiAngles"][PlotType].extend(PhiAngles)
  366             OptionsInfo["PlotTypesInfo"]["PsiAngles"][PlotType].extend(PsiAngles)
  367         
  368     # Delete MolName object
  369     pymol.cmd.delete(MolName)
  370 
  371     MiscUtil.PrintInfo("\nTotal number of phi and psi angles: %d" % TotalResCount)
  372 
  373     MiscUtil.PrintInfo("")
  374     for PlotType in  OptionsInfo["PlotTypesInfo"]["Types"]:
  375         MiscUtil.PrintInfo("Number of \"%s\" phi and psi angles: %s" % (PlotType, OptionsInfo["PlotTypesInfo"]["ResCount"][PlotType]))
  376     
  377     if not TotalResCount:
  378         MiscUtil.PrintInfo("")
  379         MiscUtil.PrintWarning("No valid phi and psi angles found in input file. Ramachandran plots will be generated without phi and psi scatter plots...")
  380         
  381 def ProcessPsiInfo(PhiPsiInfo, Precision):
  382     """Process phi and psi angels for scatter plots. """
  383 
  384     PhiAngles = []
  385     PsiAngles = []
  386     for ResNum in PhiPsiInfo["ResNums"]:
  387         Phi = "%.*f" % (Precision, PhiPsiInfo["Phi"][ResNum])
  388         Psi = "%.*f" % (Precision, PhiPsiInfo["Psi"][ResNum])
  389         PhiAngles.append(float(Phi))
  390         PsiAngles.append(float(Psi))
  391 
  392     return PhiAngles, PsiAngles
  393 
  394 def RetrieveInfileInfo():
  395     """Retrieve information for input file."""
  396     
  397     Infile = OptionsInfo["Infile"]
  398     InfileRoot = OptionsInfo["InfileRoot"]
  399     
  400     ChainsAndLigandsInfo = PyMOLUtil.GetChainsAndLigandsInfo(Infile, InfileRoot)
  401     OptionsInfo["ChainsAndLigandsInfo"] = ChainsAndLigandsInfo
  402 
  403 def ProcessChainIDs():
  404     """Process specified chain IDs for infile."""
  405     
  406     MiscUtil.PrintInfo("\nProcessing specified chain IDs for input file %s..." % OptionsInfo["Infile"])        
  407     ChainsAndLigandsInfo = OptionsInfo["ChainsAndLigandsInfo"]
  408     SpecifiedChainsAndLigandsInfo = PyMOLUtil.ProcessChainsAndLigandsOptionsInfo(ChainsAndLigandsInfo, "-c, --chainIDs", OptionsInfo["ChainIDs"], None, None)
  409     
  410     OptionsInfo["SpecifiedChainsAndLigandsInfo"] = SpecifiedChainsAndLigandsInfo
  411     
  412     MiscUtil.PrintInfo("Specified chain IDs: %s" % (", ".join(SpecifiedChainsAndLigandsInfo["ChainIDs"])))
  413 
  414 def SetupPlotsInfo():
  415     """Setup information for generating plots. """
  416 
  417     InitializePlotTypesInfo()
  418     SetupPlotTypesOutfiles()
  419     
  420     ProessContourLevelsAndColors()
  421 
  422 def ProessContourLevelsAndColors():
  423     """Process specified contour levels and colors. """
  424 
  425     if re.match("^auto$", OptionsInfo["LevelsAndColors"], re.I):
  426         return
  427     
  428     # Setup canonical plot types for validation...
  429     CanonicalPlotTypes = {}
  430     for PlotType in OptionsInfo["PlotTypesInfo"]["Types"]:
  431         CanonicalPlotTypes[PlotType.lower()] = PlotType
  432     
  433     LevelsAndColors = re.sub(" ", "", OptionsInfo["LevelsAndColors"])
  434     if not len(LevelsAndColors):
  435         MiscUtil.PrintError("The levels and colors specified using \"-l, --levelsAndColors\" option are empty.")
  436         
  437     for TypeLevelsColorsWord in LevelsAndColors.split(";"):
  438         if not len(TypeLevelsColorsWord):
  439             MiscUtil.PrintError("The plot types, levels, and colors, \"%s\" specified using \"-l, --levelsAndColors\" option in, \"%s\", is empty." % (TypeLevelsColorsWord, LevelsAndColors))
  440             
  441         TypeLevelsColorsWords = TypeLevelsColorsWord.split(":")
  442         if len(TypeLevelsColorsWords) !=2:
  443             MiscUtil.PrintError("The format of plot type, levels, and colors specification, \"%s\", specified using \"-l, --levelsAndColors\" option, in \"%s\",  is not valid: Supported format: <PlotType>: <Level>, <Color>, <Level>,..." % (TypeLevelsColorsWord, OptionsInfo["LevelsAndColors"]))
  444         
  445         PlotType = TypeLevelsColorsWords[0]
  446         if not len(PlotType):
  447             MiscUtil.PrintError("The plot type, \"%s\" specified using \"-l, --levelsAndColors\" option in, \"%s\", is empty." % (PlotType, TypeLevelsColorsWord))
  448         CanonicalPlotType = PlotType.lower()
  449         
  450         if not CanonicalPlotType in CanonicalPlotTypes:
  451             MiscUtil.PrintError("The plot type, \"%s\" specified using \"-l, --levelsAndColors\" option in, \"%s\", is not valid. Supported valus: %s" % (PlotType, TypeLevelsColorsWord, ", ".join(OptionsInfo["PlotTypesInfo"]["Types"])))
  452         PlotType = CanonicalPlotTypes[CanonicalPlotType]
  453             
  454         LevelsColorsWords = TypeLevelsColorsWords[1].split(",")
  455         if not (len(LevelsColorsWords) % 2):
  456             MiscUtil.PrintError("The format of levels and colors specifification, \"%s\", specified using \"-l, --levelsAndColors\" option in, \"%s\",  is not valid. It must contain odd number of values. Supported format: <PlotType>: <Level>, <Color>, <Level>,..." % (", ".join(LevelsColorsWords), TypeLevelsColorsWord))
  457 
  458         # Retrieve levels and colors...
  459         Levels = []
  460         Colors = []
  461         for Index, SpecWord in enumerate(LevelsColorsWords):
  462             if not len(SpecWord):
  463                 MiscUtil.PrintError("The level or color, \"%s\" specified using \"-l, --levelsAndColors\" option in, \"%s\", is empty." % (SpecWord, TypeLevelsColorsWord))
  464             
  465             if Index % 2:
  466                 Colors.append(SpecWord)
  467                 continue
  468             
  469             # Process level...
  470             if not MiscUtil.IsFloat(SpecWord):
  471                 MiscUtil.PrintError("The level, \"%s\" specified using \"-l, --levelsAndColors\" option in, \"%s\", must be a number." % (SpecWord, TypeLevelsColorsWord))
  472             
  473             Level = float(SpecWord)
  474             if len(Levels):
  475                 # The current level must be greater than all previous levels..
  476                 for PreviousLevel in Levels:
  477                     if Level <= PreviousLevel:
  478                         MiscUtil.PrintError("The level, \"%s\" specified using \"-l, --levelsAndColors\" option in, \"%s\", must be greater than all previous levels." % (SpecWord, TypeLevelsColorsWord))
  479 
  480             Levels.append(Level)
  481         
  482         OptionsInfo["PlotTypesInfo"]["Levels"][PlotType] = Levels
  483         OptionsInfo["PlotTypesInfo"]["Colors"][PlotType] = Colors
  484         
  485 def InitializePlotTypesInfo():
  486     """Initialize information for generating plots. """
  487 
  488     PlotTypesInfo = {}
  489     PlotTypesInfo["Types"] = []
  490     PlotTypesInfo["PhiPsiDensityFiles"] = {}
  491     PlotTypesInfo["PhiPsiDensityInfo"] = {}
  492     PlotTypesInfo["PhiPsiContourInfo"] = {}
  493     
  494     PlotTypesInfo["Titles"] = {}
  495     PlotTypesInfo["DrawXLabel"] = {}
  496     PlotTypesInfo["DrawYLabel"] = {}
  497     
  498     PlotTypesInfo["Limits"] = {}
  499     PlotTypesInfo["MajorTickPositions"] = {}
  500     PlotTypesInfo["MajorTickLabels"] = {}
  501     PlotTypesInfo["MinorTickPositions"] = {}
  502     
  503     PlotTypesInfo["Levels"] = {}
  504     PlotTypesInfo["Colors"] = {}
  505     PlotTypesInfo["Outfiles"] = {}
  506     PlotTypesInfo["PhiAngles"] = {}
  507     PlotTypesInfo["PsiAngles"] = {}
  508 
  509     MayaChemToolsDataDir = MiscUtil.GetMayaChemToolsLibDataPath()
  510 
  511     # Setup contour colors for supported default schemes...
  512     ContourColorSchemes = {}
  513     ContourColorSchemes["General"] = {"MuttedColorShades1": ["#FFFFFF", "#EBF1DE", "#C3D69B"],
  514                                       "MuttedColorShades2": ["#FFFFFF", "#EBF1DE", "#D7E4BD"],
  515                                       "BrightColorShades": ["#FFFFFF", "#B3E8FF", "#7FD9FF"]}
  516     ContourColorSchemes["Glycine"] = {"MuttedColorShades1": ["#FFFFFF", "#FDEADA", "#FAC090"],
  517                                       "MuttedColorShades2": ["#FFFFFF", "#FDEADA", "#FCD5B5"],
  518                                       "BrightColorShades": ["#FFFFFF", "#FFE8C5", "#FFCC7F"]}
  519     ContourColorSchemes["Proline"] = {"MuttedColorShades1": ["#FFFFFF", "#E6E0EC", "#B3A2C7"],
  520                                       "MuttedColorShades2": ["#FFFFFF", "#E6E0EC", "#CCC1DA"],
  521                                       "BrightColorShades": ["#FFFFFF", "#D0FFC5", "#7FFF8C"]}
  522     ContourColorSchemes["PreProline"] = {"MuttedColorShades1": ["#FFFFFF", "#DCE6F2", "#95B3D7"],
  523                                          "MuttedColorShades2": ["#FFFFFF", "#DCE6F2", "#B9CDE5"],
  524                                          "BrightColorShades": ["#FFFFFF", "#B3E8FF", "#7FD9FF"]}
  525 
  526     if re.match("^MuttedColorShades1$", OptionsInfo["LevelsAndColorsScheme"], re.I):
  527         DefaultColorScheme = "MuttedColorShades1"
  528     elif re.match("^MuttedColorShades2$", OptionsInfo["LevelsAndColorsScheme"], re.I):
  529         DefaultColorScheme = "MuttedColorShades2"
  530     elif re.match("^BrightColorShades$", OptionsInfo["LevelsAndColorsScheme"], re.I):
  531         DefaultColorScheme = "BrightColorShades"
  532     else:
  533         MiscUtil.PrintError("The color scheme, %s, specified using \"--levelsAndColorsScheme\" option is not supported." % (OptionsInfo["LevelsAndColorsScheme"]))
  534     
  535     for Type in ["General", "Glycine", "Proline", "PreProline"]:
  536         PlotTypesInfo["Types"].append(Type)
  537         
  538         # Setup phi and psi density file...
  539         DensityFile = os.path.join(MayaChemToolsDataDir, "PhiPsiDensity%s.csv" % (Type))
  540         if not os.path.exists(DensityFile):
  541             MiscUtil.PrintError("The phi and psi density file file, %s, doesn't exist. This is required for generating contour plots.\n" % (DensityFile))
  542         PlotTypesInfo["PhiPsiDensityFiles"][Type] = DensityFile
  543         
  544         # Setup plot title...
  545         Title = Type
  546         if re.match("^PreProline$", Type, re.I):
  547             Title = "pre-Proline"
  548         PlotTypesInfo["Titles"][Type] = Title
  549 
  550         # Setup flags for drawing axis labels...
  551         DrawXLabel, DrawYLabel = [True] * 2
  552         if not OptionsInfo["MultipleOutFiles"]:
  553             # Turn off XLabel for plots in first row...
  554             DrawXLabel = False if re.match("^(General|Glycine)$", Type, re.I) else True
  555             
  556             # Turn off YLabel for plots in second column...
  557             DrawYLabel = False if re.match("^(Glycine|PreProline)$", Type, re.I) else True
  558         PlotTypesInfo["DrawXLabel"][Type] = DrawXLabel
  559         PlotTypesInfo["DrawYLabel"][Type] = DrawYLabel
  560 
  561         # Setup limits...
  562         (MinLimit, MaxLimit) = [-180, 180]
  563         PlotTypesInfo["Limits"][Type] = [MinLimit, MaxLimit]
  564 
  565         # Setup major tick labels and positions...
  566         MajorTickPositions = list(range(MinLimit, MaxLimit, OptionsInfo["TicksMajorInterval"]))
  567         MajorTickPositions.append(MaxLimit)
  568         MajorTickLabels = ["%s" % Position for Position in MajorTickPositions]
  569         PlotTypesInfo["MajorTickPositions"][Type] = MajorTickPositions
  570         PlotTypesInfo["MajorTickLabels"][Type] = MajorTickLabels
  571 
  572         # Setup minor tick positions without any labels...
  573         MinorTickPositions = list(range(MinLimit, MaxLimit, OptionsInfo["TicksMinorInterval"]))
  574         MinorTickPositions.append(MaxLimit)
  575         PlotTypesInfo["MinorTickPositions"][Type] = MinorTickPositions
  576 
  577         # Setup contour levels and colors...
  578         Levels = []
  579         Colors = []
  580         if re.match("^General$", Type, re.I):
  581             Levels = [0.0, 0.0005, 0.02, 1.0]
  582             Colors = ContourColorSchemes[Type][DefaultColorScheme]
  583         elif re.match("^Glycine$", Type, re.I):
  584             Levels = [0.0, 0.002, 0.02, 1.0]
  585             Colors = ContourColorSchemes[Type][DefaultColorScheme]
  586         elif re.match("^Proline$", Type, re.I):
  587             Levels = [0.0, 0.002,  0.02, 1.0]
  588             Colors = ContourColorSchemes[Type][DefaultColorScheme]
  589         elif re.match("^PreProline$", Type, re.I):
  590             Levels = [0.0, 0.002, 0.02, 1.0]
  591             Colors = ContourColorSchemes[Type][DefaultColorScheme]
  592         
  593         PlotTypesInfo["Levels"][Type] = Levels
  594         PlotTypesInfo["Colors"][Type] = Colors
  595 
  596     OptionsInfo["PlotTypesInfo"] = PlotTypesInfo
  597 
  598 def SetupPlotTypesOutfiles():
  599     """Setup output file names for plot types """
  600 
  601     OptionsInfo["OutfilesList"] = []
  602     OptionsInfo["OutfilesList"].append(OptionsInfo["Outfile"])
  603 
  604     OptionsInfo["PlotTypesInfo"]["Outfiles"] = {}
  605     for PlotType in  OptionsInfo["PlotTypesInfo"]["Types"]:
  606         OptionsInfo["PlotTypesInfo"]["Outfiles"][PlotType] = None
  607 
  608     if not OptionsInfo["MultipleOutFiles"]:
  609         return
  610 
  611     FileDir, FileName, FileExt = MiscUtil.ParseFileName(OptionsInfo["Outfile"])
  612     OutfileRoot = FileName
  613     OutfileExt = FileExt
  614     
  615     for PlotType in  OptionsInfo["PlotTypesInfo"]["Types"]:
  616         PlotOutfile = "%s_%s.%s" % (OutfileRoot, PlotType, OutfileExt)
  617         if os.path.exists(PlotOutfile):
  618             if not OptionsInfo["Overwrite"]:
  619                 MiscUtil.PrintError("The plot output file, %s, already exist. Use option \"--ov\" or \"--overwrite\" and try again.\n" % (PlotOutfile))
  620                 
  621         OptionsInfo["PlotTypesInfo"]["Outfiles"][PlotType] = PlotOutfile
  622         OptionsInfo["OutfilesList"].append(PlotOutfile)
  623         
  624 def ProcessOptions():
  625     """Process and validate command line arguments and options"""
  626 
  627     MiscUtil.PrintInfo("Processing options...")
  628     
  629     # Validate options...
  630     ValidateOptions()
  631     
  632     OptionsInfo["OutMode"] = Options["--outMode"]
  633     OptionsInfo["MultipleOutFiles"] = True if re.match("^MultipleFiles$", OptionsInfo["OutMode"], re.I) else False
  634     MultipleOutFiles = OptionsInfo["MultipleOutFiles"]
  635 
  636     OptionsInfo["FigDPI"] = int(Options["--figDPI"])
  637     
  638     FigSize = Options["--figSize"]
  639     Width = 6.4
  640     Height = 4.8 if MultipleOutFiles else 6.4
  641     
  642     if not re.match("^auto$", FigSize, re.I):
  643         FigSizeWords = FigSize.split(",")
  644         Width = float(FigSizeWords[0])
  645         Height = float(FigSizeWords[1])
  646     OptionsInfo["FigSize"] = FigSize
  647     OptionsInfo["FigWidth"] = Width
  648     OptionsInfo["FigHeight"] = Height
  649     
  650     OptionsInfo["FontFamily"] = Options["--fontFamily"]
  651     OptionsInfo["FontAxesSize"] = Options["--fontAxesSize"]
  652     OptionsInfo["FontAxesWeight"] = Options["--fontAxesWeight"]
  653     OptionsInfo["FontTicksSize"] = Options["--fontTicksSize"]
  654     OptionsInfo["FontTicksWeight"] = Options["--fontTicksWeight"]
  655     OptionsInfo["FontTitleSize"] = Options["--fontTitleSize"]
  656     OptionsInfo["FontTitleWeight"] = Options["--fontTitleWeight"]
  657     
  658     OptionsInfo["Greek"] = True if re.match("^Yes$", Options["--greek"], re.I) else False
  659     
  660     OptionsInfo["Grid"] = True if re.match("^Yes$", Options["--grid"], re.I) else False
  661     OptionsInfo["GridLineColor"] = Options["--gridLineColor"]
  662     OptionsInfo["GridLineStyle"] = Options["--gridLineStyle"]
  663     OptionsInfo["GridLineWidth"] = float(Options["--gridLineWidth"])
  664     
  665     OptionsInfo["Infile"] = Options["--infile"]
  666     FileDir, FileName, FileExt = MiscUtil.ParseFileName(OptionsInfo["Infile"])
  667     OptionsInfo["InfileRoot"] = FileName
  668     
  669     OptionsInfo["LevelsAndColorsScheme"] = Options["--levelsAndColorsScheme"]
  670     
  671     OptionsInfo["Outfile"] = Options["--outfile"]
  672     FileDir, FileName, FileExt = MiscUtil.ParseFileName(OptionsInfo["Outfile"])
  673     OptionsInfo["OutfileRoot"] = FileName
  674     
  675     OptionsInfo["Overwrite"] = Options["--overwrite"]
  676     OptionsInfo["Precision"] = int(Options["--precision"])
  677     
  678     OptionsInfo["ScatterMarkerColor"] = Options["--scatterMarkerColor"]
  679     OptionsInfo["ScatterMarkerSize"] = float(Options["--scatterMarkerSize"])
  680     OptionsInfo["ScatterMarkerStyle"] = Options["--scatterMarkerStyle"]
  681 
  682     TicksMajorInterval = 90 if MultipleOutFiles else 180
  683     if not re.match("^auto$", Options["--ticksMajorInterval"], re.I):
  684         TicksMajorInterval = int(Options["--ticksMajorInterval"])
  685     OptionsInfo["TicksMajorInterval"] = TicksMajorInterval
  686         
  687     OptionsInfo["TicksMinor"] = True if re.match("^Yes$", Options["--ticksMinor"], re.I) else False
  688     TicksMinorInterval = 10 if MultipleOutFiles else 45
  689     if not re.match("^auto$", Options["--ticksMinorInterval"], re.I):
  690         TicksMinorInterval = int(Options["--ticksMinorInterval"])
  691     OptionsInfo["TicksMinorInterval"] = TicksMinorInterval
  692     
  693     RetrieveInfileInfo()
  694     OptionsInfo["ChainIDs"] = Options["--chainIDs"]
  695     
  696     ProcessChainIDs()
  697     
  698     OptionsInfo["LevelsAndColors"] = Options["--levelsAndColors"]
  699     SetupPlotsInfo()
  700 
  701 def RetrieveOptions(): 
  702     """Retrieve command line arguments and options"""
  703     
  704     # Get options...
  705     global Options
  706     Options = docopt(_docoptUsage_)
  707     
  708     # Set current working directory to the specified directory...
  709     WorkingDir = Options["--workingdir"]
  710     if WorkingDir:
  711         os.chdir(WorkingDir)
  712     
  713     # Handle examples option...
  714     if "--examples" in Options and Options["--examples"]:
  715         MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_))
  716         sys.exit(0)
  717 
  718 def ValidateOptions():
  719     """Validate option values"""
  720 
  721     MiscUtil.ValidateOptionIntegerValue("--figDPI", Options["--figDPI"], {">": 0})
  722     if not re.match("^auto$", Options["--figSize"], re.I):
  723         MiscUtil.ValidateOptionNumberValues("--figSize", Options["--figSize"], 2, ",", "float", {">": 0})
  724     
  725     MiscUtil.ValidateOptionFilePath("-i, --infile", Options["--infile"])
  726     MiscUtil.ValidateOptionFileExt("-i, --infile", Options["--infile"], "pdb cif")
  727 
  728     MiscUtil.ValidateOptionTextValue("-g, --greek", Options["--greek"], "yes no")
  729     
  730     MiscUtil.ValidateOptionTextValue("--grid", Options["--grid"], "yes no")
  731     MiscUtil.ValidateOptionFloatValue("--gridLineWidth", Options["--gridLineWidth"], {">": 0})
  732     
  733     MiscUtil.ValidateOptionTextValue("--levelsAndColorsScheme", Options["--levelsAndColorsScheme"], "MuttedColorShades1 MuttedColorShades2 BrightColorShades")
  734     
  735     MiscUtil.ValidateOptionsOutputFileOverwrite("-o, --outfile", Options["--outfile"], "--overwrite", Options["--overwrite"])
  736     
  737     MiscUtil.ValidateOptionTextValue("--outMode", Options["--outMode"], "SingleFile MultipleFiles")
  738     MiscUtil.ValidateOptionIntegerValue("-p, --precision", Options["--precision"], {">": 0})
  739     
  740     MiscUtil.ValidateOptionFloatValue("--scatterMarkerSize", Options["--scatterMarkerSize"], {">": 0})
  741 
  742     if not re.match("^auto$", Options["--ticksMajorInterval"], re.I):
  743         MiscUtil.ValidateOptionIntegerValue("--ticksMajorInterval", Options["--ticksMajorInterval"], {">": 0, "<": 360})
  744     
  745     MiscUtil.ValidateOptionTextValue("--ticksMinor", Options["--ticksMinor"], "yes no")
  746     if not re.match("^auto$", Options["--ticksMinorInterval"], re.I):
  747         MiscUtil.ValidateOptionIntegerValue("--ticksMinorInterval", Options["--ticksMinorInterval"], {">": 0, "<": 360})
  748 
  749 # Setup a usage string for docopt...
  750 _docoptUsage_ = """
  751 PyMOLGenerateRamachandranPlots.py - Generate Ramachandran plots
  752 
  753 Usage:
  754     PyMOLGenerateRamachandranPlots.py [--chainIDs <First, All or ID1,ID2...>]
  755                                       [--figDPI <number>] [--figSize <width, height>] [--fontFamily <text>]
  756                                       [--fontAxesSize <number or text>] [--fontAxesWeight <number or text>]
  757                                       [--fontTicksSize <number or text>] [--fontTicksWeight <number or text>]
  758                                       [--fontTitleSize <number or text>] [--fontTitleWeight <number or text>] [--greek <yes or no>]
  759                                       [--grid <yes or no>] [--gridLineColor <text>] [--gridLineStyle <text>] [--gridLineWidth <number>]
  760                                       [--levelsAndColors <PlotType:Level,color,Level,...;...>] [--levelsAndColorsScheme <text>]
  761                                       [--outMode <SingleFile or MultipleFiles>] [--overwrite]  [--precision <number>]
  762                                       [--scatterMarkerColor <text>] [--scatterMarkerSize <number>] [--scatterMarkerStyle <text>]
  763                                       [--ticksMajorInterval <number>] [--ticksMinor <yes or no>] [--ticksMinorInterval <number>]
  764                                       [-w <dir>] -i <infile> -o <outfile>
  765     PyMOLGenerateRamachandranPlots.py -h | --help | -e | --examples
  766 
  767 Description:
  768     Generate Ramachandran plots for amino acid residues present in macromolecules.
  769     
  770     The Ramachandran plots are generated by plotting phi and psi backbone angles
  771     corresponding to the following four categories of amino acids:
  772     
  773         General: All residues except glycine, proline, or pre-proline
  774         Glycine: Only glycine residues
  775         Proline: Only proline residues
  776         PreProline: Only residues before proline not including glycine or
  777             proline
  778     
  779     In addition to the scatter plots for phi and psi angles, the filled contours
  780     are generated for the density of phi and psi angles [ Ref 144 ] for the
  781     Ramachandran plots. The contours are generated for "favored" and "allowed"
  782     regions. The phi and psi density is retrieved from the following density files
  783     available in MAYACHEMTOOLS/lib/data/ directory:
  784     
  785         General: PhiPsiDensityGeneral.csv 
  786         Glycine: PhiPsiDensityGlycine.csv
  787         Proline: PhiPsiDensityProline.csv
  788         PreProline: PhiPsiDensityPreProline.csv
  789     
  790     The supported input  file format are: PDB (.pdb), mmCIF (.cif)
  791     
  792     The output image file can be saved in any format supported by Python
  793     module Matplotlib. The image format is automatically detected from the
  794     output file extension. 
  795     
  796     Some of the most common output image file formats are: EPS (.eps), PDF (.pdf),
  797     PNG (.png), PS (.ps), SVG (.svg).
  798 
  799 Options:
  800     -c, --chainIDs <First, All or ID1,ID2...>  [default: All]
  801         List of chain IDs to use for calculating phi and psi angles for residues
  802         in chains. Possible values: First, All, or a comma delimited list of chain
  803         IDs. The default is to use all chain IDs in input file.
  804     -e, --examples
  805         Print examples.
  806     --figDPI <number>  [default: 300]
  807         Figure resolution in dots per inches. The DPI value must be supported
  808         by Matplotlib during generation of an image of a specific format. No
  809         validation is performed.
  810     --figSize <width, height>  [default: auto]
  811         Figure dimensions in inches. The default values are dependent on the
  812         the value of '--outMode' option as shown below:
  813         
  814             SingleFile: 6.4, 6.4
  815             MultipleFiles: 6.4, 4.8
  816         
  817     --fontFamily <text>  [default: auto]
  818         Font family to use for title, axes labels, and tick marks. It must be a
  819         valid Matplotlib value. The default value corresponds to the value 
  820         plt.rcParams["font.family"] in your environment. For example: serif,
  821         sans-serif, cursive, etc. 
  822     --fontAxesSize <number or text>  [default: 10]
  823         Font size for labels on axes. It must be valid Matplotlib font size. For
  824         example: size in points, xx-small, x-small, small, medium, etc.
  825     --fontAxesWeight <number or text>  [default: regular]
  826         Font weight for labels on axes. It must be valid Matplotlib value. For
  827         example: a numeric value in range 0-1000, ultralight, light, normal,
  828         regular, book, medium, etc.
  829     --fontTicksSize <number or text>  [default: 8]
  830         Font size for tick labels. It must be a valid Matplotlib font size. For
  831         example: size in points, xx-small, x-small, small, medium, etc.
  832     --fontTicksWeight <number or text>  [default: regular]
  833         Font weight for tick labels. It must be valid Matplotlib value. For
  834         example: a numeric value in range 0-1000, ultralight, light,
  835         normal, regular, book, medium, etc.
  836     --fontTitleSize <number or text>  [default: 10]
  837         Font size for title. It must be a valid Matplotlib font size. For example:
  838         size in points, xx-small, x-small, small, medium, etc.
  839     --fontTitleWeight <number or text>  [default: bold]
  840         Font weight for title. It must be a valid Matplotlib value. For example: a
  841         numeric value in range 0-1000, ultralight, light, normal, regular, book,
  842         medium, etc.
  843     -g, --greek <yes or no>  [default: yes]
  844         Show phi and psi labels as greek characters.
  845     --grid <yes or no>  [default: yes]
  846         Display grid lines at major tick marks.
  847     --gridLineColor <text>  [default: #b0b0b0]
  848         Grid line color. It must be a valid Matplotlib value. The default color
  849         is light gray.
  850     --gridLineStyle <text>  [default: dotted]
  851         Grid line style. It must be a valid Matplotlib value. For example:
  852         '-' or 'solid', --' or 'dashed', '-.' or 'dashdot', ':' or 'dotted' etc.
  853     --gridLineWidth <number>  [default: 0.8]
  854         Grid line width. It must be a valid Matplotlib value.
  855     -h, --help
  856         Print this help message.
  857     -i, --infile <infile>
  858         Input file name.
  859     -l, --levelsAndColors <PlotType:Level,color,Level,...;...>  [default: auto]
  860         Semicolon delimited list of contour levels and colors for four types
  861         of Ramachandran plots.
  862         
  863         Three default contour level and color scheme may be specified by
  864         '--levelsAndColorsScheme' option. By default, the 'MuttedColorShades1'
  865         scheme is used. The default contour levels correspond to 'favored' and
  866         'allowed' regions [ Ref 144 ] for phi and psi angles.
  867         
  868         The colors are used to fill spaces between contour levels. The values
  869         for contour levels must be ascending order. The number of colors
  870         must be one less than the number contour levels.
  871         
  872         The format of contour level and color specification is as follows:
  873         
  874             PlotType:Level,Color,Level,...;PlotType:Level,Color,Level,...
  875         
  876         The valid values for plot type are:
  877         
  878             General, Glycine, Proline, or PreProline
  879         
  880         The contour level must be a number. The color value must be a valid color
  881         name or a hexadecimal color string supported by Matplotlib. No validation
  882         is performed.
  883         
  884         For example:
  885         
  886             General: 0.0, #FFFFFF, 0.0005, #EBF1DE, 0.02, #C3D69B, 1.0
  887         
  888     --levelsAndColorsScheme <text>  [default: MuttedColorShades1]
  889         Default contour levels and colors scheme.  Possible values:
  890         MuttedColorShades1, MuttedColorShades2, or BrightColorShades.
  891         
  892         This option is only used during 'auto' value of '--levelsAndColors' option.
  893         The default contour levels correspond to 'favored' and 'allowed' regions
  894         [ Ref 144 ] for phi and psi angles.
  895         
  896         The default contour and color values for different default schemes are
  897         shown below:
  898         
  899         MuttedColorShades1:
  900         
  901             General: 0.0, #FFFFFF, 0.0005, #EBF1DE, 0.02, #C3D69B, 1.0
  902             Glycine: 0.0, #FFFFFF, 0.002, #7FD9FF, 0.02, #FAC090, 1.0
  903             Proline: 0.0, #FFFFFF, 0.002, #E6E0EC, 0.02, #B3A2C7, 1.0
  904             PreProline: 0.0, #FFFFFF, 0.002, #DCE6F2, 0.02, #95B3D7, 1.0
  905         
  906         MuttedColorShades2:
  907         
  908             General: 0.0, #FFFFFF, 0.0005, #EBF1DE, 0.02, #D7E4BD, 1.0
  909             Glycine: 0.0, #FFFFFF, 0.002, #FDEADA, 0.02, #FCD5B5, 1.0
  910             Proline: 0.0, #FFFFFF, 0.002, #E6E0EC, 0.02, #CCC1DA, 1.0
  911             PreProline: 0.0, #FFFFFF, 0.002, #DCE6F2, 0.02, #B9CDE5, 1.0
  912         
  913         BrightColorShades: [ Ref 145 ]
  914         
  915             General: 0.0, #FFFFFF, 0.0005, #B3E8FF, 0.02, #7FD9FF, 1.0
  916             Glycine: 0.0, #FFFFFF, 0.002, #FFE8C5, 0.02, #FFCC7F, 1.0
  917             Proline: 0.0, #FFFFFF, 0.002, #D0FFC5, 0.02, #7FFF8C, 1.0
  918             PreProline: 0.0, #FFFFFF, 0.002, #B3E8FF, 0.02, #7FD9FF, 1.0
  919         
  920     -o, --outfile <outfile>
  921         Output image file name.
  922         
  923         A set of output files is optionally generated for 'MultipleFiles' value of
  924         '--outMode' option. The names of these output files are automatically
  925         generated from the the name of the specified output file as shown
  926         below:
  927         
  928             General: <OutfileRoot>_General.<OutfileExt>
  929             Glycine: <OutfileRoot>_Glycine.<OutfileExt>
  930             Proline: <OutfileRoot>_Proline.<OutfileExt>
  931             PreProline: <OutfileRoot>_PreProline.<OutfileExt>
  932         
  933     --outMode <Single or Multiple>  [default: SingleFile]
  934         A single output file containing all four Ramachandran plots or multiple
  935         output files corresponding to different types of Ramachandran plots.
  936         
  937         The phi and psi angles are categorized into the following groups
  938         corresponding to four types of Ramachandran plots:
  939         
  940             General: All residues except glycine, proline, or pre-proline
  941             Glycine: Only glycine residues
  942             Proline: Only proline residues
  943             PreProline: Only residues before proline not including glycine or
  944                 proline
  945         
  946     --overwrite
  947         Overwrite existing files.
  948     -p, --precision <number>  [default: 2]
  949         Floating point precision for plotting the calculated phi and psi angles.
  950     --scatterMarkerColor <text>  [default: #1f77b4]
  951         Scatter marker color for plotting to phi and psi angles. It must be a
  952         valid Matplotlib value. The default color is dark blue.
  953     --scatterMarkerSize <number>  [default: 1.0]
  954         Scatter marker size for piloting phi and psi angles. It must be a valid
  955         Matplotlib value.
  956     --scatterMarkerStyle <text>  [default: .]
  957         Scatter marker style for piloting phi and psi angles. It must be a valid
  958         Matplotlib value. For example: '.' (point), ',' (pixel), 'o' (circle), etc.
  959     --ticksMajorInterval <number>  [default: auto]
  960         Display major marks on axes at intervals specified in degrees for phi and
  961         psi angles. The default value is dependent on the the value of '--outMode'
  962         option: SingleFile: 180; MultipleFiles: 90
  963         
  964         The grid lines are drawn at the locations of major tick marks.
  965     --ticksMinor <yes or no>  [default: yes]
  966         Display minor tick marks. The major tick mark are always displayed.
  967     --ticksMinorInterval <number>  [default: auto]
  968         Display minor marks on axes at intervals specified in degrees for phi and
  969         psi angles. The default value is dependent on the the value of '--outMode'
  970         option: SingleFile:  45; MultipleFiles: 10
  971     -w, --workingdir <dir>
  972         Location of working directory which defaults to the current directory.
  973 
  974 Examples:
  975     To generate Ramachandran plot for all residues across all chains in input
  976     file and write out a single SVG file containing all four types of plots, type:
  977 
  978         % PyMOLGenerateRamachandranPlots.py -i Sample3.pdb -o Sample3Out.svg
  979 
  980     To generate Ramachandran plot for all residues across all chains in input
  981     file and write out four SVG files corresponding to four types of plots, type:
  982 
  983         % PyMOLGenerateRamachandranPlots.py --outMode MultipleFiles
  984           -i Sample3.pdb -o Sample3Out.svg
  985 
  986     To generate Ramachandran plot for all residues in a specific chain in input
  987     file and write out a single PDF file containing all four types of plots, type:
  988 
  989         % PyMOLGenerateRamachandranPlots.py -c E -i Sample3.pdb
  990           -o Sample3Out.pdf
  991 
  992     To generate Ramachandran plot for all residues across all chains in input
  993     file using specific options and write out four PNG files containing all four
  994     types of plots, type:
  995 
  996         % PyMOLGenerateRamachandranPlots.py --outMode MultipleFiles
  997           --figSize "6,4" --figDPI 600 --fontTitleSize 10 --fontTitleWeight
  998           normal --greek no --grid no --levelsAndColors
  999           "General: 0.0, #FFFFFF, 0.0005, #B3E8FF, 0.02, #7FD9FF, 1.0"
 1000           -i Sample3.pdb -o Sample3Out.png
 1001 
 1002 Author:
 1003     Manish Sud(msud@san.rr.com)
 1004 
 1005 See also:
 1006     DownloadPDBFiles.pl, PyMOLCalculatePhiPsiAngles.py, PyMOLCalculateRMSD.py,
 1007     PyMOLCalculateProperties.py
 1008 
 1009 Copyright:
 1010     Copyright (C) 2021 Manish Sud. All rights reserved.
 1011 
 1012     The functionality available in this script is implemented using PyMOL, a
 1013     molecular visualization system on an open source foundation originally
 1014     developed by Warren DeLano.
 1015 
 1016     This file is part of MayaChemTools.
 1017 
 1018     MayaChemTools is free software; you can redistribute it and/or modify it under
 1019     the terms of the GNU Lesser General Public License as published by the Free
 1020     Software Foundation; either version 3 of the License, or (at your option) any
 1021     later version.
 1022 
 1023 """
 1024 
 1025 if __name__ == "__main__":
 1026     main()