def GetInterfaceChainsAndResiduesInfo(MolName1, ChainIDs1, MolName2, ChainIDs2,
                                      Method, Cutoff):
    """Get interface chains and residues info for chains using a specified methodology."""

    InterfaceChainsResiduesInfo1 = None
    InterfaceChainsResiduesInfo2 = None

    ChainNames1 = ",".join(ChainIDs1)
    ChainNames2 = ",".join(ChainIDs2)

    if re.match("^BySASAChange$", Method, re.I):
        InterfaceChainsResiduesInfo1, InterfaceChainsResiduesInfo2 = PyMOLUtil.GetInterfaceChainsResiduesBySASAChange(
            MolName1, ChainNames1, MolName2, ChainNames2, Cutoff)
    elif re.match("^ByHeavyAtomsDistance$", Method, re.I):
        InterfaceChainsResiduesInfo1, InterfaceChainsResiduesInfo2 = PyMOLUtil.GetnterfaceChainsResiduesByHeavyAtomsDistance(
            MolName1, ChainNames1, MolName2, ChainNames2, Cutoff)
    elif re.match("^ByCAlphaAtomsDistance$", Method, re.I):
        InterfaceChainsResiduesInfo1, InterfaceChainsResiduesInfo2 = PyMOLUtil.GetInterfaceChainsResiduesByCAlphaAtomsDistance(
            MolName1, ChainNames1, MolName2, ChainNames2, Cutoff)
    else:
        MiscUtil.PrintError(
            "Failed to retrieve interface residues information: Method %s is not valid..."
            % Method)

    return InterfaceChainsResiduesInfo1, InterfaceChainsResiduesInfo2
def GetPocketSelectionResiduesInfo(MolName, ChainID, LigandResName,
                                   LigandResNum, PocketDistanceCutoff,
                                   SelectionType):
    """Get pocket residues info for a specified selection type. """

    SelectionInfo = None

    if re.match("^Pockets$", SelectionType, re.I):
        SelectionInfo = PyMOLUtil.GetPocketPolymerResiduesInfo(
            MolName, ChainID, LigandResName, LigandResNum,
            PocketDistanceCutoff)
    elif re.match("^PocketSolvents$", SelectionType, re.I):
        SelectionInfo = PyMOLUtil.GetPocketSolventResiduesInfo(
            MolName, ChainID, LigandResName, LigandResNum,
            PocketDistanceCutoff)
    elif re.match("^PocketInorganics$", SelectionType, re.I):
        SelectionInfo = PyMOLUtil.GetPocketInorganicResiduesInfo(
            MolName, ChainID, LigandResName, LigandResNum,
            PocketDistanceCutoff)
    else:
        MiscUtil.PrintError(
            "Failed to retrieve pocket residues information: Selection type %s is not valid..."
            % SelectionType)

    return SelectionInfo
def GetSelectionResiduesInfo(MolName, ChainID, SelectionType):
    """Get residues info for a specified selection type. """

    SelectionInfo = None
    SelectionLabel = None

    if re.match("^Ligands$", SelectionType, re.I):
        SelectionLabel = "ligand"
        SelectionInfo = PyMOLUtil.GetLigandResiduesInfo(
            MolName, ChainID) if ChainID is not None else None
    elif re.match("^Solvents$", SelectionType, re.I):
        SelectionLabel = "solvent"
        SelectionInfo = PyMOLUtil.GetSolventResiduesInfo(
            MolName, ChainID) if ChainID is not None else None
    elif re.match("^Inorganics$", SelectionType, re.I):
        SelectionLabel = "inorganic"
        SelectionInfo = PyMOLUtil.GetInorganicResiduesInfo(
            MolName, ChainID) if ChainID is not None else None
    elif re.match("^Chains$", SelectionType, re.I):
        SelectionLabel = "polymer chain"
        SelectionInfo = PyMOLUtil.GetPolymerResiduesInfo(
            MolName, ChainID) if ChainID is not None else None
    elif re.match("^NonStandardAminoAcids$", SelectionType, re.I):
        SelectionLabel = "non-standard amino acids"
        SelectionInfo = PyMOLUtil.GetAminoAcidResiduesInfo(
            MolName, ChainID, "NonStandard") if ChainID is not None else None
    else:
        MiscUtil.PrintError(
            "Failed to retrieve residues information: Selection type %s is not valid..."
            % SelectionType)

    return SelectionInfo, SelectionLabel
def ProcessChainIDs():
    """Process specified chain IDs for infiles."""
    
    OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"] = []
    OptionsInfo["InfilesInfo"]["ChainSelections"] = []
    
    for FileIndex in range(0, len(OptionsInfo["InfilesInfo"]["InfilesNames"])):
        MiscUtil.PrintInfo("\nProcessing specified chain IDs for input file %s..." % OptionsInfo["InfilesInfo"]["InfilesNames"][FileIndex])
        
        ChainsAndLigandsInfo = OptionsInfo["InfilesInfo"]["ChainsAndLigandsInfo"][FileIndex]
        SpecifiedChainsAndLigandsInfo = PyMOLUtil.ProcessChainsAndLigandsOptionsInfo(ChainsAndLigandsInfo, "-c, --chainIDs", OptionsInfo["ChainIDs"], None, None)

        # Setup chain selections...
        ChainSelections = None
        if not OptionsInfo["AllChains"]:
            Chains = []
            for ChainID in SpecifiedChainsAndLigandsInfo["ChainIDs"]:
                Chains.append("chain %s" % ChainID)
            ChainSelections = " or ".join(Chains)
            ChainSelections = "(%s)" % ChainSelections
        
        OptionsInfo["InfilesInfo"]["SpecifiedChainsAndLigandsInfo"].append(SpecifiedChainsAndLigandsInfo)
        OptionsInfo["InfilesInfo"]["ChainSelections"].append(ChainSelections)
        
        MiscUtil.PrintInfo("Specified chain IDs: %s" % (", ".join(SpecifiedChainsAndLigandsInfo["ChainIDs"])))
def GetFormattedPropertyValue(Selection, Name):
    """Calculate and return a formatted property value. """

    Quiet = OptionsInfo["Quiet"]
    Precision = OptionsInfo["Precision"]
    
    Value = None
    if re.match("^CenterOfMass$", Name, re.I):
        Value = PyMOLUtil.CalculateCenterOfMass(Selection, Quiet)
    elif re.match("^MolecularWeight$", Name, re.I):
        Value = pymol.util.compute_mass(Selection, implicit = False, quiet = Quiet)
    elif re.match("^MolecularSurfaceArea$", Name, re.I):
        Value = pymol.util.get_area(Selection, -1, 0, quiet = Quiet)
    elif re.match("^SumOfFormalCharges$", Name, re.I):
        Value = pymol.util.sum_formal_charges(Selection, quiet = Quiet)
    elif re.match("^SumOfPartialCharges$", Name, re.I):
        Value = pymol.util.sum_partial_charges(Selection, quiet = Quiet)
    elif re.match("^SolventAccessibleSurfaceArea$", Name, re.I):
        Value = pymol.util.get_sasa(Selection, quiet = Quiet)
    else:
        MiscUtil.PrintError("The property name specified, %s, using \"-m, --mode\" option is not a valid name." % Name)

    if Value is None:
        FormattedValue = "NA"
    else:
        if type(Value) is list:
            FormattedValues = []
            for ListElement in Value:
                FormattedListElement = "%.*f" % (Precision, ListElement)
                FormattedValues.append(FormattedListElement)
            FormattedValue = " ".join(FormattedValues)
        else:
            FormattedValue = "%.*f" % (Precision, Value)
        
    return FormattedValue
def ListFileInfo(Infile):
    """List information for macromolecules in a file."""

    FileDir, FileName, FileExt = MiscUtil.ParseFileName(Infile)
    MolName = FileName

    # Load infile...
    pymol.cmd.load(Infile, MolName)

    ChainIDs = PyMOLUtil.GetChains(MolName)
    ListHeaderInfo(Infile)

    ListChainsInfo(MolName, ChainIDs)
    ListChainsResiduesInfo(MolName, ChainIDs)

    ListLigandsInfo(MolName, ChainIDs)
    ListSolventsInfo(MolName, ChainIDs)
    ListInorganicsInfo(MolName, ChainIDs)

    ListPocketsInfo(MolName, ChainIDs)

    ListInterfaceResiduesInfo(MolName, ChainIDs)
    ListSurfaceResiduesInfo(MolName, ChainIDs)
    ListPhiPsiAnglesInfo(MolName, ChainIDs)

    ListBoundingBoxInfo(MolName)

    # Delete infile object...
    pymol.cmd.delete(MolName)

    ListFileSizeAndModificationInfo(Infile)
def ListPocketsInfo(MolName, ChainIDs):
    """List pockect residues information across all chains."""

    if not (OptionsInfo["All"] or OptionsInfo["PocketLigands"] or
            OptionsInfo["PocketSolvents"] or OptionsInfo["PocketInorganics"]):
        return

    for ChainID in ChainIDs:
        MiscUtil.PrintInfo(
            "\nListing ligand pockets information for chain %s..." % (ChainID))

        LigandsInfo = PyMOLUtil.GetLigandResiduesInfo(MolName, ChainID)
        if not len(LigandsInfo["ResNames"]):
            MiscUtil.PrintInfo("\nNumber of residues in ligand pocket: None")
            MiscUtil.PrintInfo("Chain ID: %s; Ligands: None" % (ChainID))
            continue

        for LigandResName in sorted(LigandsInfo["ResNames"]):
            for LigandResNum in LigandsInfo["ResNum"][LigandResName]:
                ListPocketsPolymerInfo(MolName, ChainID, LigandResName,
                                       LigandResNum)
                ListPocketsSolventsInfo(MolName, ChainID, LigandResName,
                                        LigandResNum)
                ListPocketsInorganicsInfo(MolName, ChainID, LigandResName,
                                          LigandResNum)
Esempio n. 8
0
def RetrieveInfileInfo():
    """Retrieve information for input file."""
    
    Infile = OptionsInfo["Infile"]
    InfileRoot = OptionsInfo["InfileRoot"]
    
    ChainsAndLigandsInfo = PyMOLUtil.GetChainsAndLigandsInfo(Infile, InfileRoot)
    OptionsInfo["ChainsAndLigandsInfo"] = ChainsAndLigandsInfo
Esempio n. 9
0
def ProcessChainIDs():
    """Process specified chain IDs for infile."""
    
    MiscUtil.PrintInfo("\nProcessing specified chain IDs for input file %s..." % OptionsInfo["Infile"])        
    ChainsAndLigandsInfo = OptionsInfo["ChainsAndLigandsInfo"]
    SpecifiedChainsAndLigandsInfo = PyMOLUtil.ProcessChainsAndLigandsOptionsInfo(ChainsAndLigandsInfo, "-c, --chainIDs", OptionsInfo["ChainIDs"], None, None)
    
    OptionsInfo["SpecifiedChainsAndLigandsInfo"] = SpecifiedChainsAndLigandsInfo
    
    MiscUtil.PrintInfo("Specified chain IDs: %s" % (", ".join(SpecifiedChainsAndLigandsInfo["ChainIDs"])))
def ProcessChainAndLigandIDs():
    """Process chain and ligand IDs"""

    MolName = OptionsInfo["InfileRoot"]
    ChainsAndLigandsInfo = PyMOLUtil.GetChainsAndLigandsInfo(
        OptionsInfo["Infile"], MolName)
    OptionsInfo["ChainsAndLigandsInfo"] = ChainsAndLigandsInfo

    MiscUtil.PrintInfo(
        "\nProcessing specified chain and ligand IDs for input file %s..." %
        OptionsInfo["Infile"])

    SpecifiedChainsAndLigandsInfo = PyMOLUtil.ProcessChainsAndLigandsOptionsInfo(
        ChainsAndLigandsInfo, "-c, --chainIDs", OptionsInfo["ChainIDs"],
        "-l, --ligandIDs", OptionsInfo["LigandIDs"])
    OptionsInfo[
        "SpecifiedChainsAndLigandsInfo"] = SpecifiedChainsAndLigandsInfo

    CheckPresenceOfValidLigandIDs(ChainsAndLigandsInfo,
                                  SpecifiedChainsAndLigandsInfo)
def ConvertLigandFileFormat():
    """Comvert ligand file format."""

    Infile = OptionsInfo["Infile"]
    Outfile = OptionsInfo["Outfile"]

    MiscUtil.PrintInfo("\nGenerating file %s..." % Outfile)

    PyMOLUtil.ConvertFileFormat(Infile, Outfile)

    if not os.path.exists(Outfile):
        MiscUtil.PrintWarning("Failed to generate Outfile file, %s..." %
                              (Outfile))
Esempio n. 12
0
def GeneratePSEFile():
    """Comvert PML file to PSE file."""

    PMLFile = OptionsInfo["Infile"]
    PSEFile = OptionsInfo["Outfile"]

    MiscUtil.PrintInfo("\nGenerating file %s..." % PSEFile)

    PyMOLUtil.ConvertPMLFileToPSEFile(PMLFile,
                                      PSEFile,
                                      OutputFeedback=OptionsInfo["Feedback"])

    if not os.path.exists(PSEFile):
        MiscUtil.PrintWarning("Failed to generate PSE file, %s..." % (PSEFile))
def ListPhiPsiAnglesInfo(MolName, ChainIDs):
    """List phi and psi torsion angles for polymer chains with in a molecule."""

    if not (OptionsInfo["All"] or OptionsInfo["PhiPsi"]):
        return

    MiscUtil.PrintInfo("\nListing phi and psi angles information...")

    if not len(ChainIDs):
        MiscUtil.PrintInfo("\nNumber of phi and psi angles: None\n")
        return

    for ChainID in ChainIDs:
        if re.match("^Categories$", OptionsInfo["PhiPsiMode"], re.I):
            # Retrieve phi and psi angles by categories used for Ramachandran plots
            GeneralPhiPsiInfo, GlyPhiPsiInfo, ProPhiPsiInfo, PreProPhiPsiInfo = PyMOLUtil.GetPhiPsiCategoriesResiduesInfo(
                MolName, ChainID)

            SetupAndListPhiPsiResiduesInfo(
                GeneralPhiPsiInfo,
                "General (All residues except glycine, proline, or pre-proline)",
                ChainID)
            SetupAndListPhiPsiResiduesInfo(GlyPhiPsiInfo,
                                           "Glycine (Only glycine residues)",
                                           ChainID)
            SetupAndListPhiPsiResiduesInfo(ProPhiPsiInfo,
                                           "Proline (Only proline residues)",
                                           ChainID)
            SetupAndListPhiPsiResiduesInfo(
                PreProPhiPsiInfo,
                "Pre-Proline (Only residues before proline not including glycine or proline)",
                ChainID)
        else:
            PhiPsiResiduesInfo = PyMOLUtil.GetPhiPsiResiduesInfo(
                MolName, ChainID, Categorize=True)
            SetupAndListPhiPsiResiduesInfo(PhiPsiResiduesInfo, "All", ChainID)
Esempio n. 14
0
def CalculatePhiPsiAngles():
    """Calculate phi and psi angles for macromolecules containing amino acids."""
    
    SetupOutputFiles()
    WriteColumnLabels()
    
    Infile = OptionsInfo["Infile"]
    MolName = OptionsInfo["InfileRoot"]
    
    MiscUtil.PrintInfo("\nCalculating phi and psi torsion angles for input file %s..." % Infile)
    
    # Load infile
    pymol.cmd.load(Infile, MolName)

    OutDelim = OptionsInfo["OutDelim"]
    Precision = OptionsInfo["Precision"]

    # Go over specified chain IDs..
    for ChainID in OptionsInfo["SpecifiedChainsAndLigandsInfo"]["ChainIDs"]:
        # Write out information for combined file...
        PhiPsiInfo = PyMOLUtil.GetPhiPsiResiduesInfo(MolName, ChainID, Categorize = True)
        OptionsInfo["OutfileResCount"] += len(PhiPsiInfo["ResNums"])
        WritePhiPsiInfo(OptionsInfo["OutFH"], MolName, ChainID, PhiPsiInfo, OutDelim, Precision)

        # Write out information for category fies...
        if OptionsInfo["MultipleOutFiles"]:
            PhiPsiInfoList = []
            GeneralPhiPsiInfo, GlycinePhiPsiInfo, ProlinePhiPsiInfo, PreProlinePhiPsiInfo = PyMOLUtil.GetPhiPsiCategoriesResiduesInfo(MolName, ChainID)
            PhiPsiInfoList.extend([GeneralPhiPsiInfo, GlycinePhiPsiInfo, ProlinePhiPsiInfo, PreProlinePhiPsiInfo])
            
            for Index, Category in enumerate(OptionsInfo["Categories"]):
                OptionsInfo["CategoriesResCount"][Category] += len(PhiPsiInfoList[Index]["ResNums"])
                WritePhiPsiInfo(OptionsInfo["CategoriesOutFHs"][Category], MolName, ChainID, PhiPsiInfoList[Index], OutDelim, Precision)
    
    # Delete MolName object
    pymol.cmd.delete(MolName)

    # Close all files...
    CloseOutputFiles()

    # List number of phi and psi angles in output files...
    MiscUtil.PrintInfo("\nNumber of phi and psi angles in output file %s: %d" % (OptionsInfo["Outfile"],  OptionsInfo["OutfileResCount"]))
    if OptionsInfo["MultipleOutFiles"]:
        MiscUtil.PrintInfo("")
        for Index, Category in enumerate(OptionsInfo["Categories"]):
            MiscUtil.PrintInfo("Number of phi and psi angles in output file %s: %d" % (OptionsInfo["CategoriesOutfiles"][Category], OptionsInfo["CategoriesResCount"][Category]))
Esempio n. 15
0
def RetrieveChainIDs(Infile, InfileRoot):
    """Retrieve chains IDs for an input file."""

    pymol.cmd.reinitialize()

    MolName = InfileRoot
    pymol.cmd.load(Infile, MolName)

    ChainIDs = PyMOLUtil.GetChains(MolName)
    pymol.cmd.delete(MolName)

    if ChainIDs is None:
        ChainIDs = []

    # Print out chain and ligand IDs...
    ChainInfo = ", ".join(ChainIDs) if len(ChainIDs) else "None"
    MiscUtil.PrintInfo("Chain IDs: %s" % ChainInfo)

    return ChainIDs
def ListSurfaceResiduesInfo(MolName, ChainIDs):
    """List surface and buried residues for polymer chains with in a molecule."""

    if not (OptionsInfo["All"] or OptionsInfo["SurfaceResidues"]):
        return

    MiscUtil.PrintInfo("\nListing surface and buried residues information...")
    if not len(ChainIDs):
        MiscUtil.PrintInfo(
            "\nNumber of surface residues: None\nNumber of buried residues: None"
        )
        return

    TotalSurfaceResidues, TotalBuriedResidues = [0] * 2

    for ChainID in ChainIDs:
        SurfaceResiduesSelectionInfo, BuriedResiduesSelectionInfo = PyMOLUtil.GetSurfaceAndBuriedResiduesInfo(
            MolName, ChainID, OptionsInfo["SurfaceResiduesCutoff"])

        SurfaceResiduesCount, SurfaceResiduesDistribution, SurfaceResiduesIDs = SetupSelectionResiduesInfo(
            SurfaceResiduesSelectionInfo)
        BuriedResiduesCount, BuriedResiduesDistribution, BuriedResiduesIDs = SetupSelectionResiduesInfo(
            BuriedResiduesSelectionInfo)

        TotalSurfaceResidues += SurfaceResiduesCount
        MiscUtil.PrintInfo("\nChainID: %s; Number of surface residues: %d" %
                           (ChainID, SurfaceResiduesCount))
        MiscUtil.PrintInfo("Residue distribution: %s" %
                           (SurfaceResiduesDistribution))
        if OptionsInfo["SurfaceResiduesIDs"]:
            MiscUtil.PrintInfo("Residue IDs: %s" % (SurfaceResiduesIDs))

        TotalBuriedResidues += BuriedResiduesCount
        MiscUtil.PrintInfo("\nChainID: %s; Number of buried residues: %d" %
                           (ChainID, BuriedResiduesCount))
        MiscUtil.PrintInfo("Residue distribution: %s" %
                           (BuriedResiduesDistribution))
        if OptionsInfo["SurfaceResiduesIDs"]:
            MiscUtil.PrintInfo("Residue IDs: %s" % (BuriedResiduesIDs))

    MiscUtil.PrintInfo(
        "\nTotal number of surface residues: %d\nTotal number of buried residues: %s"
        % (TotalSurfaceResidues, TotalBuriedResidues))
def RetrieveInfilesInfo():
    """Retrieve information for input files."""

    InfilesInfo = {}
    
    InfilesInfo["InfilesNames"] = []
    InfilesInfo["InfilesRoots"] = []
    InfilesInfo["ChainsAndLigandsInfo"] = []
    
    for Infile in OptionsInfo["InfilesNames"]:
        FileDir, FileName, FileExt = MiscUtil.ParseFileName(Infile)
        InfileRoot = FileName
        ChainsAndLigandInfo = PyMOLUtil.GetChainsAndLigandsInfo(Infile, InfileRoot)
        
        InfilesInfo["InfilesNames"].append(Infile)
        InfilesInfo["InfilesRoots"].append(InfileRoot)
        InfilesInfo["ChainsAndLigandsInfo"].append(ChainsAndLigandInfo)
    
    OptionsInfo["InfilesInfo"] = InfilesInfo
Esempio n. 18
0
def RetrieveChainsIDs():
    """Retrieve chain IDs. """

    MolName = OptionsInfo["InfileRoot"]
    Infile = OptionsInfo["Infile"]
    
    MiscUtil.PrintInfo("\nRetrieving chains information for input file %s..." % Infile)

    LoadMolecule(Infile, MolName)

    ChainIDs = PyMOLUtil.GetChains(MolName)
    
    DeleteMolecule(MolName)

    if ChainIDs is None:
        ChainIDs = []

    # Print out chain and ligand IDs...
    ChainInfo = ", ".join(ChainIDs) if len(ChainIDs) else "None"
    MiscUtil.PrintInfo("Chain IDs: %s" % ChainInfo)
                         
    OptionsInfo["ChainIDs"] = ChainIDs
Esempio n. 19
0
def ProcessSpecifiedMutations():
    """Process specified mutations"""
    
    MiscUtil.PrintInfo("\nProcessing specified mutations...")

    SpecifiedMutationsInfo = []

    Mutations = re.sub(" ", "", OptionsInfo["Mutations"])
    MutationsWords = Mutations.split(",")
    if not len(MutationsWords):
        MiscUtil.PrintError("The number of comma delimited mutations specified using \"-m, --mutations\" option, \"%s\",  must be > 0." % (OptionsInfo["Mutations"]))

    # Load macromolecule from input file...
    MolName = OptionsInfo["InfileRoot"]
    LoadMolecule(OptionsInfo["Infile"], MolName)
    
    FirstMutation = True
    CurrentChainID = None
    CanonicalMutationMap = {}
    MutationsCount, ValidMutationsCount = [0] * 2
    
    for Mutation in MutationsWords:
        MutationsCount += 1
        if not len(Mutation):
            MiscUtil.PrintWarning("The mutation, \"%s\", specified using \"-m, --mutations\" option is empty.\nIgnoring mutation..." % (Mutation))
            continue

        CanonicalMutation = Mutation.lower()
        if CanonicalMutation in CanonicalMutationMap:
            MiscUtil.PrintWarning("The mutation, \"%s\", specified using \"-m, --mutations\" option already exist.\nIgnoring mutation..." % (Mutation))
            continue
        CanonicalMutationMap[CanonicalMutation] = Mutation
            
        # Match with a chain ID...
        MatchedResults = re.match(r"^([a-z0-9]+):([a-z]+)([0-9]+)([a-z]+)$", Mutation, re.I)
        if not MatchedResults:
            # Match without a chain ID...
            MatchedResults = re.match(r"^([a-z]+)([0-9]+)([a-z]+)$", Mutation, re.I)
            
        if not MatchedResults:
            MiscUtil.PrintWarning("The format of mutation, \"%s\", specified using \"-m, --mutations\" option is not valid. Supported format: <ChainID>:<ResName><ResNum><ResName> or <ResName><ResNum><ResName>\nIgnoring mutation..." % (Mutation))
            continue

        NumOfMatchedGroups =  len(MatchedResults.groups())
        if NumOfMatchedGroups == 3:
            ResName, ResNum, NewResName = MatchedResults.groups()
        elif NumOfMatchedGroups == 4:
            CurrentChainID, ResName, ResNum, NewResName = MatchedResults.groups()
        else:
            MiscUtil.PrintWarning("The format of mutation, \"%s\", specified using \"-m, --mutations\" option is not valid. Supported format: <ChainID>:<ResName><ResNum><ResName> or <ResName><ResNum><ResName>\nIgnoring mutation..." % (Mutation))
            continue
        
        if FirstMutation:
            FirstMutation = False
            if  CurrentChainID is None:
                MiscUtil.PrintError("The first mutation, \"%s\", specified using \"-m, --mutations\" option must be colon delimited and contain only two values, the first value corresponding to chain ID" % (Mutation))
        
        ResName = ResName.upper()
        NewResName = NewResName.upper()
        
        # Is ResNum and ResName present in input file?
        SelectionCmd = "%s and chain %s and resi %s and resn %s" % (MolName, CurrentChainID, ResNum, ResName)
        ResiduesInfo = PyMOLUtil.GetSelectionResiduesInfo(SelectionCmd)
        if (ResiduesInfo is None) or (not len(ResiduesInfo["ResNames"])):
            MiscUtil.PrintWarning("The residue name, %s, and residue number, %s, in mutation, \"%s\", specified using \"-m, --mutations\" option appears to be missing in input file.\nIgnoring mutation..." % (ResName, ResNum, Mutation))
            continue

        ValidMutationsCount += 1
        
        # Track mutation information...
        SpecifiedMutationsInfo.append([Mutation, CurrentChainID, ResName, ResNum, NewResName])
        
    # Delete macromolecule...
    DeleteMolecule(MolName)

    MiscUtil.PrintInfo("\nTotal number of mutations: %d" % MutationsCount)
    MiscUtil.PrintInfo("Number of valid mutations: %d" % ValidMutationsCount)
    MiscUtil.PrintInfo("Number of ignored mutations: %d" % (MutationsCount - ValidMutationsCount))
    
    if not len(SpecifiedMutationsInfo):
        MiscUtil.PrintError("No valid mutations, \"%s\" specified using \"-m, --mutations\" option." % (OptionsInfo["Mutations"]))
        
    OptionsInfo["SpecifiedMutationsInfo"] = SpecifiedMutationsInfo
def CalculatePhiPsiAngles():
    """Calculate phi and psi angles for scatter plots."""

    Infile = OptionsInfo["Infile"]
    MolName = OptionsInfo["InfileRoot"]

    # Load molecule...
    pymol.cmd.reinitialize()
    pymol.cmd.load(Infile, MolName)

    MiscUtil.PrintInfo(
        "\nCalculating phi and psi torsion angles for input file %s..." %
        Infile)

    # Initialize...
    OptionsInfo["PlotTypesInfo"]["PhiAngles"] = {}
    OptionsInfo["PlotTypesInfo"]["PsiAngles"] = {}
    OptionsInfo["PlotTypesInfo"]["ResCount"] = {}
    for PlotType in OptionsInfo["PlotTypesInfo"]["Types"]:
        OptionsInfo["PlotTypesInfo"]["PhiAngles"][PlotType] = []
        OptionsInfo["PlotTypesInfo"]["PsiAngles"][PlotType] = []
        OptionsInfo["PlotTypesInfo"]["ResCount"][PlotType] = 0

    Precision = OptionsInfo["Precision"]

    TotalResCount = 0
    # Go over specified chain IDs..
    for ChainID in OptionsInfo["SpecifiedChainsAndLigandsInfo"]["ChainIDs"]:
        PhiPsiInfoList = []
        GeneralPhiPsiInfo, GlycinePhiPsiInfo, ProlinePhiPsiInfo, PreProlinePhiPsiInfo = PyMOLUtil.GetPhiPsiCategoriesResiduesInfo(
            MolName, ChainID)
        PhiPsiInfoList.extend([
            GeneralPhiPsiInfo, GlycinePhiPsiInfo, ProlinePhiPsiInfo,
            PreProlinePhiPsiInfo
        ])

        for Index, PlotType in enumerate(
                OptionsInfo["PlotTypesInfo"]["Types"]):
            PhiPsiInfo = PhiPsiInfoList[Index]
            ResCount = len(PhiPsiInfo["ResNums"])
            if not ResCount:
                continue

            TotalResCount += ResCount
            OptionsInfo["PlotTypesInfo"]["ResCount"][PlotType] += ResCount

            PhiAngles, PsiAngles = ProcessPsiInfo(PhiPsiInfo, Precision)
            OptionsInfo["PlotTypesInfo"]["PhiAngles"][PlotType].extend(
                PhiAngles)
            OptionsInfo["PlotTypesInfo"]["PsiAngles"][PlotType].extend(
                PsiAngles)

    # Delete MolName object
    pymol.cmd.delete(MolName)

    MiscUtil.PrintInfo("\nTotal number of phi and psi angles: %d" %
                       TotalResCount)

    MiscUtil.PrintInfo("")
    for PlotType in OptionsInfo["PlotTypesInfo"]["Types"]:
        MiscUtil.PrintInfo(
            "Number of \"%s\" phi and psi angles: %s" %
            (PlotType, OptionsInfo["PlotTypesInfo"]["ResCount"][PlotType]))

    if not TotalResCount:
        MiscUtil.PrintInfo("")
        MiscUtil.PrintWarning(
            "No valid phi and psi angles found in input file. Ramachandran plots will be generated without phi and psi scatter plots..."
        )