       1 #!/bin/env python
       2 #
       3 # File: RDKitCalculateMolecularDescriptors.py
       4 # Author: Manish Sud <msud@san.rr.com>
       5 #
       6 # Copyright (C) 2018 Manish Sud. All rights reserved.
       7 #
       8 # The functionality available in this script is implemented using RDKit, an
       9 # open source toolkit for cheminformatics developed by Greg Landrum.
      10 #
      11 # This file is part of MayaChemTools.
      12 #
      13 # MayaChemTools is free software; you can redistribute it and/or modify it under
      14 # the terms of the GNU Lesser General Public License as published by the Free
      15 # Software Foundation; either version 3 of the License, or (at your option) any
      16 # later version.
      17 #
      18 # MayaChemTools is distributed in the hope that it will be useful, but without
      19 # any warranty; without even the implied warranty of merchantability of fitness
      20 # for a particular purpose.  See the GNU Lesser General Public License for more
      21 # details.
      22 #
      23 # You should have received a copy of the GNU Lesser General Public License
      24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
      25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
      26 # Boston, MA, 02111-1307, USA.
      27 #
      28
      29 from __future__ import print_function
      30
      31 # Add local python path to the global path and import standard library modules...
      32 import os
      33 import sys;  sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), "..", "lib", "Python"))
      34 import time
      35 import re
      36
      37 # RDKit imports...
      38 try:
      39     from rdkit import rdBase
      40     from rdkit import Chem
      41     from rdkit.Chem import AllChem
      42     from rdkit.Chem import rdMolDescriptors
      43     from rdkit.Chem import Descriptors
      44     from rdkit.Chem import Descriptors3D
      45 except ImportError as ErrMsg:
      46     sys.stderr.write("\nFailed to import RDKit module/package: %s\n" % ErrMsg)
      47     sys.stderr.write("Check/update your RDKit environment and try again.\n\n")
      48     sys.exit(1)
      49
      50 # RDKit dependency imports...
      51 import numpy
      52
      53 # MayaChemTools imports...
      54 try:
      55     from docopt import docopt
      56     import MiscUtil
      57     import RDKitUtil
      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 DescriptorNamesMap = {}
      68
      69 def main():
      70     """Start execution of the script"""
      71
      72     MiscUtil.PrintInfo("\n%s (RDK v%s; %s): Starting...\n" % (ScriptName, rdBase.rdkitVersion, time.asctime()))
      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     CalculateMolecularDescriptors()
      84
      85     MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName)
      86     MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime))
      87
      88 def CalculateMolecularDescriptors():
      89     """Calculate molecular descriptors."""
      90
      91     ProcessMolecularDescriptorsInfo()
      92     PerformCalculations()
      93
      94 def ProcessMolecularDescriptorsInfo():
      95     """Process descriptors information."""
      96
      97     RetrieveMolecularDescriptorsInfo()
      98     ProcessSpecifiedDescriptorNames()
      99
     100 def PerformCalculations():
     101     """Calculate descriptors for a specified list of descriptors."""
     102
     103     Infile = OptionsInfo["Infile"]
     104     Outfile = OptionsInfo["Outfile"]
     105
     106     DescriptorNames = OptionsInfo["SpecifiedDescriptorNames"]
     107     DescriptorCount = len(DescriptorNames)
     108
     109     TextOutFileMode = OptionsInfo["TextOutFileMode"]
     110     TextOutFileDelim = OptionsInfo["TextOutFileDelim"]
     111     SMILESOut = OptionsInfo["SMILESOut"]
     112
     113     Precision = OptionsInfo["Precision"]
     114
     115     MiscUtil.PrintInfo("Calculating %d molecular descriptor(s) for each molecule..." % DescriptorCount)
     116
     117     # Setup a molecule reader...
     118     MiscUtil.PrintInfo("\nProcessing file %s..." % Infile)
     119     Mols = RDKitUtil.ReadMolecules(Infile, **OptionsInfo["InfileParams"])
     120
     121     # Setup a writer...
     122     Compute2DCoords = OptionsInfo["OutfileParams"]["Compute2DCoords"]
     123     if TextOutFileMode:
     124         Writer = open(Outfile, "w")
     125     else:
     126         Writer = RDKitUtil.MoleculesWriter(Outfile, **OptionsInfo["OutfileParams"])
     127
     128     if Writer is None:
     129         MiscUtil.PrintError("Failed to setup a writer for output fie %s " % Outfile)
     130
     131     MiscUtil.PrintInfo("Generating file %s..." % Outfile)
     132
     133     # Wite out headers for a text file...
     134     if TextOutFileMode:
     135         LineWords = []
     136         if SMILESOut:
     137             LineWords.append("SMILES")
     138         LineWords.append("MolID")
     139         LineWords.extend(DescriptorNames)
     140         Line = TextOutFileDelim.join(LineWords)
     141         Writer.write("%s\n" % Line)
     142
     143     # Process molecules...
     144     MolCount = 0
     145     ValidMolCount = 0
     146     MatchCount = 0
     147
     148     for Mol in Mols:
     149         MolCount += 1
     150         if Mol is None:
     151             continue
     152
     153         if RDKitUtil.IsMolEmpty(Mol):
     154             MolName = RDKitUtil.GetMolName(Mol, MolCount)
     155             MiscUtil.PrintWarning("Ignoring empty molecule: %s" % MolName)
     156             continue
     157
     158         ValidMolCount += 1
     159
     160         # Calculate descriptors...
     161         CalculatedValues = []
     162         for Index in range(0, DescriptorCount):
     163             Name = DescriptorNames[Index]
     164             ComputeFunction = DescriptorNamesMap["ComputeFunction"][Name]
     165             Value = FormatCalculatedValue(ComputeFunction(Mol), Precision)
     166             CalculatedValues.append(Value)
     167
     168         # Write out calculated values...
     169         if TextOutFileMode:
     170             LineWords = []
     171             if SMILESOut:
     172                 SMILES = Chem.MolToSmiles(Mol, isomericSmiles = True, canonical = True)
     173                 LineWords.append(SMILES)
     174
     175             MolName = RDKitUtil.GetMolName(Mol, MolCount)
     176             LineWords.append(MolName)
     177             LineWords.extend(CalculatedValues)
     178             Line = TextOutFileDelim.join(LineWords)
     179             Writer.write("%s\n" % Line)
     180         else:
     181             for Index in range(0, DescriptorCount):
     182                 Name = DescriptorNames[Index]
     183                 Value = CalculatedValues[Index]
     184                 Mol.SetProp(Name, Value)
     185
     186             if Compute2DCoords:
     187                 AllChem.Compute2DCoords(Mol)
     188
     189             Writer.write(Mol)
     190
     191     if Writer is not None:
     192         Writer.close()
     193
     194     MiscUtil.PrintInfo("\nTotal number of molecules: %d" % MolCount)
     195     MiscUtil.PrintInfo("Number of valid molecules: %d" % ValidMolCount)
     196     MiscUtil.PrintInfo("Number of ignored molecules: %d" % (MolCount - ValidMolCount))
     197
     198 def FormatCalculatedValue(Value, Precision):
     199     """Format calculated value of descriptor based on its type."""
     200
     201     if (type(Value) is float) or (type(Value) is numpy.float64):
     202         FormattedValue = "%.*f" % (Precision, Value)
     203         if not re.search("[1-9]", FormattedValue):
     204             FormattedValue = "0.0"
     205     elif type(Value) is list:
     206         FormattedValue = "%s" % Value
     207         FormattedValue = re.sub('\[|\]|,', '', FormattedValue)
     208     else:
     209         FormattedValue = "%s" % Value
     210
     211     return FormattedValue
     212
     213 def ProcessSpecifiedDescriptorNames():
     214     """Process and validate specified decriptor names."""
     215
     216     OptionsInfo["SpecifiedDescriptorNames"] = []
     217
     218     if not re.match("^(2D|3D|All|FragmentCountOnly|Specify)$", OptionsInfo["Mode"], re.I):
     219         MiscUtil.PrintError("Mode value, %s, using \"-m, --mode\" option is not a valid value." % OptionsInfo["Mode"])
     220
     221     if re.match("^2D$", OptionsInfo["Mode"], re.I):
     222         OptionsInfo["SpecifiedDescriptorNames"] = DescriptorNamesMap["2D"]["Names"]
     223         if OptionsInfo["FragmentCount"]:
     224             OptionsInfo["SpecifiedDescriptorNames"].extend(DescriptorNamesMap["FragmentCount"]["Names"])
     225         return
     226     elif re.match("^3D$", OptionsInfo["Mode"], re.I):
     227         OptionsInfo["SpecifiedDescriptorNames"] = DescriptorNamesMap["3D"]["Names"]
     228         return
     229     elif re.match("^All$", OptionsInfo["Mode"], re.I):
     230         OptionsInfo["SpecifiedDescriptorNames"] = DescriptorNamesMap["2D"]["Names"]
     231         if OptionsInfo["FragmentCount"]:
     232             OptionsInfo["SpecifiedDescriptorNames"].extend(DescriptorNamesMap["FragmentCount"]["Names"])
     233         OptionsInfo["SpecifiedDescriptorNames"].extend(DescriptorNamesMap["3D"]["Names"])
     234         return
     235     elif re.match("^FragmentCountOnly$", OptionsInfo["Mode"], re.I):
     236         OptionsInfo["SpecifiedDescriptorNames"] = DescriptorNamesMap["FragmentCount"]["Names"]
     237         return
     238
     239     # Set up a canonical descriptor names map for checking specified names...
     240     CanonicalNameMap = {}
     241     for Name in  DescriptorNamesMap["ComputeFunction"]:
     242         CanonicalNameMap[Name.lower()] = Name
     243
     244     # Parse and validate specified names...
     245     DescriptorNames = re.sub(" ", "", OptionsInfo["DescriptorNames"])
     246     if not DescriptorNames:
     247         MiscUtil.PrintError("No descriptor names specified for \"-d, --descriptorNames\" option")
     248
     249     SMILESInfile = MiscUtil.CheckFileExt(Options["--infile"], "smi")
     250     Canonical3DNameMap = {}
     251     if SMILESInfile:
     252         for Name in DescriptorNamesMap["3D"]["Names"]:
     253             Canonical3DNameMap[Name.lower()] = Name
     254
     255     SpecifiedDescriptorNames = []
     256     for Name in DescriptorNames.split(","):
     257         CanonicalName = Name.lower()
     258         if CanonicalName in CanonicalNameMap:
     259             SpecifiedDescriptorNames.append(CanonicalNameMap[CanonicalName])
     260         else:
     261             MiscUtil.PrintError("The descriptor name, %s, specified using \"-d, --descriptorNames\" option is not a valid name." % (Name))
     262         if SMILESInfile:
     263             if CanonicalName in Canonical3DNameMap:
     264                 MiscUtil.PrintError("The 3D descriptor name, %s, specified using \"-d, --descriptorNames\" option is not a valid for SMILES input file." % (Name))
     265
     266     if not len(SpecifiedDescriptorNames):
     267         MiscUtil.PrintError("No valid descriptor name specified for \"-d, --descriptorNames\" option")
     268
     269     OptionsInfo["SpecifiedDescriptorNames"] = SpecifiedDescriptorNames
     270
     271 def RetrieveMolecularDescriptorsInfo():
     272     """Retrieve descriptors information."""
     273
     274     MiscUtil.PrintInfo("\nRetrieving information for avalible molecular descriptors...")
     275
     276     # Initialze data for 2D, FragmentCount and 3D descriptors...
     277     DescriptorNamesMap["Types"] = ["2D", "FragmentCount", "3D"]
     278     DescriptorNamesMap["ComputeFunction"] = {}
     279
     280     Autocorr2DExclude = OptionsInfo["Autocorr2DExclude"]
     281
     282     for Type in DescriptorNamesMap["Types"]:
     283         DescriptorNamesMap[Type] = {}
     284         DescriptorNamesMap[Type]["Names"] = []
     285
     286     # Setup data for 2D and FragmentCount...
     287     DescriptorsInfo = Descriptors.descList
     288     for DescriptorInfo in DescriptorsInfo:
     289         Name = DescriptorInfo[0]
     290         ComputeFunction = DescriptorInfo[1]
     291
     292         Type = "2D"
     293         if re.match("^fr_", Name, re.I):
     294             Type = "FragmentCount"
     295         elif re.match("^Autocorr2D$", Name, re.I):
     296             if Autocorr2DExclude:
     297                 continue
     298
     299         if Name in DescriptorNamesMap["ComputeFunction"]:
     300             MiscUtil.PrintWarning("Ignoring duplicate descriptor name: %s..." % Name)
     301         else:
     302             DescriptorNamesMap[Type]["Names"].append(Name)
     303             DescriptorNamesMap["ComputeFunction"][Name] = ComputeFunction
     304
     305     # Add new 2D decriptor name to the list...
     306     Type = "2D"
     307     if not Autocorr2DExclude:
     308         try:
     309             Name = "Autocorr2D"
     310             ComputeFunction =  rdMolDescriptors.CalcAUTOCORR2D
     311             if not Name in DescriptorNamesMap["ComputeFunction"]:
     312                 DescriptorNamesMap[Type]["Names"].append(Name)
     313                 DescriptorNamesMap["ComputeFunction"][Name] = ComputeFunction
     314         except (AttributeError):
     315             MiscUtil.PrintInfo("2D descriptor, %s, is not available in your current version of RDKit." % Name)
     316
     317     # Set data for 3D descriptors...
     318     Type = "3D"
     319     NameToComputeFunctionMap = {'PMI1' : Descriptors3D.PMI1, 'PMI2' : Descriptors3D.PMI2, 'PMI3' : Descriptors3D.PMI3, 'NPR1' : Descriptors3D.NPR1, 'NPR2' : Descriptors3D.NPR2, 'RadiusOfGyration' : Descriptors3D.RadiusOfGyration, 'InertialShapeFactor' :  Descriptors3D.InertialShapeFactor, 'Eccentricity' : Descriptors3D.Eccentricity, 'Asphericity' : Descriptors3D.Asphericity, 'SpherocityIndex' : Descriptors3D.SpherocityIndex}
     320
     321     for Name in NameToComputeFunctionMap:
     322         ComputeFunction = NameToComputeFunctionMap[Name]
     323         if Name in DescriptorNamesMap["ComputeFunction"]:
     324             MiscUtil.PrintWarning("Ignoring duplicate descriptor name: %s..." % Name)
     325         else:
     326             DescriptorNamesMap[Type]["Names"].append(Name)
     327             DescriptorNamesMap["ComputeFunction"][Name] = ComputeFunction
     328
     329     # Check and add new 3D descriptors not directly available through Descriptors3D module...
     330     Type = "3D"
     331     AvailableName3DMap = {}
     332     for Name in ['Autocorr3D', 'RDF', 'MORSE', 'WHIM', 'GETAWAY']:
     333         ComputeFunction = None
     334         try:
     335             if re.match("^Autocorr3D$", Name, re.I):
     336                 ComputeFunction =  rdMolDescriptors.CalcAUTOCORR3D
     337             elif re.match("^RDF$", Name, re.I):
     338                 ComputeFunction =  rdMolDescriptors.CalcRDF
     339             elif re.match("^MORSE$", Name, re.I):
     340                 ComputeFunction =  rdMolDescriptors.CalcMORSE
     341             elif re.match("^WHIM$", Name, re.I):
     342                 ComputeFunction =  rdMolDescriptors.CalcWHIM
     343             elif re.match("^GETAWAY$", Name, re.I):
     344                 ComputeFunction =  rdMolDescriptors.CalcGETAWAY
     345             else:
     346                 ComputeFunction = None
     347         except (AttributeError):
     348             MiscUtil.PrintWarning("3D descriptor, %s, is not available in your current version of RDKit" % Name)
     349
     350         if ComputeFunction is not None:
     351             AvailableName3DMap[Name] = ComputeFunction
     352
     353     for Name in AvailableName3DMap:
     354         ComputeFunction = AvailableName3DMap[Name]
     355         if not Name in DescriptorNamesMap["ComputeFunction"]:
     356             DescriptorNamesMap[Type]["Names"].append(Name)
     357             DescriptorNamesMap["ComputeFunction"][Name] = ComputeFunction
     358
     359     Count = 0
     360     TypesCount = []
     361     for Type in DescriptorNamesMap["Types"]:
     362         TypeCount = len(DescriptorNamesMap[Type]["Names"])
     363         TypesCount.append(TypeCount)
     364         Count += TypeCount
     365
     366     if not Count:
     367         MiscUtil.PrintError("Failed to retrieve any  molecular descriptors...")
     368
     369     # Sort descriptor names...
     370     for Type in DescriptorNamesMap["Types"]:
     371         DescriptorNamesMap[Type]["Names"] = sorted(DescriptorNamesMap[Type]["Names"])
     372
     373     MiscUtil.PrintInfo("\nTotal number of availble molecular descriptors: %d" % Count)
     374     for Index in range(0, len(DescriptorNamesMap["Types"])):
     375         Type = DescriptorNamesMap["Types"][Index]
     376         TypeCount = TypesCount[Index]
     377         MiscUtil.PrintInfo("Number of %s molecular descriptors: %d" % (Type, TypeCount))
     378
     379     MiscUtil.PrintInfo("")
     380
     381 def ListMolecularDescriptorsInfo():
     382     """List descriptors information."""
     383
     384     MiscUtil.PrintInfo("\nListing information for avalible molecular descriptors...")
     385
     386     Delimiter = ", "
     387     for Type in DescriptorNamesMap["Types"]:
     388         Names = DescriptorNamesMap[Type]["Names"]
     389         MiscUtil.PrintInfo("\n%s descriptors: %s" % (Type, Delimiter.join(Names)))
     390
     391     MiscUtil.PrintInfo("")
     392
     393 def ProcessOptions():
     394     """Process and validate command line arguments and options"""
     395
     396     MiscUtil.PrintInfo("Processing options...")
     397
     398     # Validate options...
     399     ValidateOptions()
     400
     401     OptionsInfo["Autocorr2DExclude"] = True
     402     if not re.match("^Yes$", Options["--autocorr2DExclude"], re.I):
     403         OptionsInfo["Autocorr2DExclude"] = False
     404
     405     OptionsInfo["FragmentCount"] = True
     406     if not re.match("^Yes$", Options["--fragmentCount"], re.I):
     407         OptionsInfo["FragmentCount"] = False
     408
     409     OptionsInfo["DescriptorNames"] = Options["--descriptorNames"]
     410     OptionsInfo["Mode"] = Options["--mode"]
     411
     412     OptionsInfo["Infile"] = Options["--infile"]
     413     OptionsInfo["InfileParams"] = MiscUtil.ProcessOptionInfileParameters("--infileParams", Options["--infileParams"], Options["--infile"])
     414
     415     OptionsInfo["Outfile"] = Options["--outfile"]
     416     OptionsInfo["OutfileParams"] = MiscUtil.ProcessOptionOutfileParameters("--outfileParams", Options["--outfileParams"], Options["--infile"], Options["--outfile"])
     417
     418     OptionsInfo["Overwrite"] = Options["--overwrite"]
     419
     420     TextOutFileMode = False
     421     TextOutFileDelim = ""
     422     if MiscUtil.CheckFileExt(Options["--outfile"], "csv"):
     423         TextOutFileMode = True
     424         TextOutFileDelim = ","
     425     elif MiscUtil.CheckFileExt(Options["--outfile"], "tsv txt"):
     426         TextOutFileMode = True
     427         TextOutFileDelim = "\t"
     428     OptionsInfo["TextOutFileMode"] = TextOutFileMode
     429     OptionsInfo["TextOutFileDelim"] = TextOutFileDelim
     430
     431     OptionsInfo["Precision"] = int(Options["--precision"])
     432
     433     OptionsInfo["SMILESOut"] = False
     434     if re.match("^Yes$", Options["--smilesOut"], re.I):
     435         OptionsInfo["SMILESOut"] = True
     436
     437 def RetrieveOptions():
     438     """Retrieve command line arguments and options"""
     439
     440     # Get options...
     441     global Options
     442     Options = docopt(_docoptUsage_)
     443
     444     # Set current working directory to the specified directory...
     445     WorkingDir = Options["--workingdir"]
     446     if WorkingDir:
     447         os.chdir(WorkingDir)
     448
     449     # Handle examples option...
     450     if "--examples" in Options and Options["--examples"]:
     451         MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_))
     452         sys.exit(0)
     453
     454     # Handle listing of descriptor information...
     455     if  Options["--list"]:
     456         ProcessListMolecularDescriptorsOption()
     457         sys.exit(0)
     458
     459 def ProcessListMolecularDescriptorsOption():
     460     """Process list descriptors information."""
     461
     462     RetrieveMolecularDescriptorsInfo()
     463     ListMolecularDescriptorsInfo()
     464
     465 def ValidateOptions():
     466     """Validate option values"""
     467
     468     MiscUtil.ValidateOptionTextValue("-a, --autocorr2DExclude", Options["--autocorr2DExclude"], "yes no")
     469     MiscUtil.ValidateOptionTextValue("-f, --fragmentCount", Options["--fragmentCount"], "yes no")
     470
     471     MiscUtil.ValidateOptionTextValue("-m, --mode", Options["--mode"], "2D 3D All FragmentCountOnly Specify")
     472
     473     if re.match("^Specify$", Options["--mode"], re.I):
     474         if re.match("^none$", Options["--descriptorNames"], re.I):
     475             MiscUtil.PrintError("The name(s) of molecular descriptors must be specified using \"-d, --descriptorNames\" option during \"Specify\" value of \"-m, --mode\" option.")
     476
     477     MiscUtil.ValidateOptionFilePath("-i, --infile", Options["--infile"])
     478     MiscUtil.ValidateOptionFileExt("-i, --infile", Options["--infile"], "sdf sd mol smi csv tsv txt")
     479
     480     if re.match("^3D|All$", Options["--mode"], re.I):
     481         if MiscUtil.CheckFileExt(Options["--infile"], "smi"):
     482             MiscUtil.PrintError("The input SMILES file, %s, is not valid for  \"3D or All\" value of \"-m, --mode\" option.")
     483
     484     MiscUtil.ValidateOptionFileExt("-o, --outfile", Options["--outfile"], "sdf sd csv tsv txt")
     485     MiscUtil.ValidateOptionsOutputFileOverwrite("-o, --outfile", Options["--outfile"], "--overwrite", Options["--overwrite"])
     486     MiscUtil.ValidateOptionsDistinctFileNames("-i, --infile", Options["--infile"], "-o, --outfile", Options["--outfile"])
     487
     488     MiscUtil.ValidateOptionIntegerValue("-p, --precision", Options["--precision"], {">": 0})
     489     MiscUtil.ValidateOptionTextValue("-s, --smilesOut", Options["--smilesOut"], "yes no")
     490
     491 # Setup a usage string for docopt...
     492 _docoptUsage_ = """
     493 RDKitCalculateMolecularDescriptors.py - Calculate 2D/3D molecular descriptors
     494
     495 Usage:
     496     RDKitCalculateMolecularDescriptors.py [--autocorr2DExclude <yes or no>] [--fragmentCount <yes or no>]
     497                                           [--descriptorNames <Name1,Name2,...>] [--infileParams <Name,Value,...>]
     498                                           [--mode <2D, 3D, All...>] [--outfileParams <Name,Value,...>]
     499                                           [--overwrite] [--precision <number>] [--smilesOut <yes or no>]
     500                                           [-w <dir>] -i <infile> -o <outfile>
     501     RDKitCalculateMolecularDescriptors.py -l | --list
     502     RDKitCalculateMolecularDescriptors.py -h | --help | -e | --examples
     503
     504 Description:
     505     Calculate 2D/3D molecular descriptors for molecules and write them out to a SD or
     506     CSV/TSV text file.
     507
     508     The complete list of currently available molecular descriptors may be obtained by
     509     using '-l, --list' option. The names of valid 2D, fragment count, and 3D molecular
     510     descriptors are shown below:
     511
     512     2D descriptors: Autocorr2D, BalabanJ, BertzCT, Chi0, Chi1, Chi0n - Chi4n, Chi0v - Chi4v,
     513     EState_VSA1 - EState_VSA11, ExactMolWt, FpDensityMorgan1, FpDensityMorgan2, FpDensityMorgan3,
     514     FractionCSP3, HallKierAlpha, HeavyAtomCount, HeavyAtomMolWt, Ipc, Kappa1 - Kappa3,
     515     LabuteASA, MaxAbsEStateIndex, MaxAbsPartialCharge, MaxEStateIndex, MaxPartialCharge,
     516     MinAbsEStateIndex, MinAbsPartialCharge, MinEStateIndex, MinPartialCharge, MolLogP,
     517     MolMR, MolWt, NHOHCount, NOCount, NumAliphaticCarbocycles, NumAliphaticHeterocycles,
     518     NumAliphaticRings, NumAromaticCarbocycles, NumAromaticHeterocycles, NumAromaticRings,
     519     NumHAcceptors, NumHDonors, NumHeteroatoms, NumRadicalElectrons, NumRotatableBonds,
     520     NumSaturatedCarbocycles, NumSaturatedHeterocycles, NumSaturatedRings, NumValenceElectrons,
     521     PEOE_VSA1 - PEOE_VSA14,  RingCount, SMR_VSA1 - SMR_VSA10, SlogP_VSA1 - SlogP_VSA12,
     522     TPSA, VSA_EState1 - VSA_EState10, qed
     523
     524     FragmentCount 2D descriptors: fr_Al_COO, fr_Al_OH, fr_Al_OH_noTert, fr_ArN, fr_Ar_COO,
     525     fr_Ar_N, fr_Ar_NH, fr_Ar_OH, fr_COO, fr_COO2, fr_C_O, fr_C_O_noCOO, fr_C_S, fr_HOCCN,
     526     fr_Imine, fr_NH0, fr_NH1, fr_NH2, fr_N_O, fr_Ndealkylation1, fr_Ndealkylation2, fr_Nhpyrrole,
     527     fr_SH, fr_aldehyde, fr_alkyl_carbamate, fr_alkyl_halide, fr_allylic_oxid, fr_amide, fr_amidine,
     528     fr_aniline, fr_aryl_methyl, fr_azide, fr_azo, fr_barbitur, fr_benzene, fr_benzodiazepine,
     529     fr_bicyclic, fr_diazo, fr_dihydropyridine, fr_epoxide, fr_ester, fr_ether, fr_furan, fr_guanido,
     530     fr_halogen, fr_hdrzine, fr_hdrzone, fr_imidazole, fr_imide, fr_isocyan, fr_isothiocyan, fr_ketone,
     531     fr_ketone_Topliss, fr_lactam, fr_lactone, fr_methoxy, fr_morpholine, fr_nitrile, fr_nitro,
     532     fr_nitro_arom, fr_nitro_arom_nonortho, fr_nitroso, fr_oxazole, fr_oxime, fr_para_hydroxylation,
     533     fr_phenol, fr_phenol_noOrthoHbond, fr_phos_acid, fr_phos_ester, fr_piperdine, fr_piperzine,
     534     fr_priamide, fr_prisulfonamd, fr_pyridine, fr_quatN, fr_sulfide, fr_sulfonamd, fr_sulfone,
     535     fr_term_acetylene, fr_tetrazole, fr_thiazole, fr_thiocyan, fr_thiophene, fr_unbrch_alkane, fr_urea
     536
     537     3D descriptors: Asphericity, Autocorr3D, Eccentricity, GETAWAY, InertialShapeFactor, MORSE,
     538     NPR1, NPR2, PMI1, PMI2, PMI3, RDF, RadiusOfGyration, SpherocityIndex, WHIM
     539
     540     The supported input file formats are: Mol (.mol), SD (.sdf, .sd), SMILES (.smi,
     541     .txt, .csv, .tsv)
     542
     543     The supported output file formats are: SD File (.sdf, .sd), CSV/TSV (.csv, .tsv, .txt)
     544
     545 Options:
     546     -a, --autocorr2DExclude <yes or no>  [default: yes]
     547         Exclude Autocorr2D descriptor from the calculation of 2D descriptors.
     548     -f, --fragmentCount <yes or no>  [default: yes]
     549         Include 2D fragment count descriptors during the calculation. These descriptors are
     550         counted using SMARTS patterns specified in FragmentDescriptors.csv file distributed
     551         with RDKit. This option is only used during '2D' or 'All' value of '-m, --mode' option.
     552     -d, --descriptorNames <Name1,Name2,...>  [default: none]
     553         A comma delimited list of supported molecular descriptor names to calculate.
     554         This option is only used during 'Specify' value of '-m, --mode' option.
     555     -e, --examples
     556         Print examples.
     557     -h, --help
     558         Print this help message.
     559     -i, --infile <infile>
     560         Input file name.
     561     --infileParams <Name,Value,...>  [default: auto]
     562         A comma delimited list of parameter name and value pairs for reading
     563         molecules from files. The supported parameter names for different file
     564         formats, along with their default values, are shown below:
     565
     566             SD, MOL: removeHydrogens,yes,sanitize,yes,strictParsing,yes
     567             SMILES: smilesColumn,1,smilesNameColumn,2,smilesDelimiter,space,
     568                 smilesTitleLine,auto,sanitize,yes
     569
     570         Possible values for smilesDelimiter: space, comma or tab.
     571     -l, --list
     572         List molecular descriptors without performing any calculations.
     573     -m, --mode <2D, 3D, All, FragmentCountOnly, or Specify>  [default: 2D]
     574         Type of molecular descriptors to calculate. Possible values: 2D, 3D,
     575         All or Specify. The name of molecular descriptors must be specified using
     576         '-d, --descriptorNames' for 'Specify'. 2D descriptors also include 1D descriptors.
     577         The structure  of molecules must contain 3D coordinates for the  calculation
     578         of 3D descriptors.
     579     -o, --outfile <outfile>
     580         Output file name.
     581     --outfileParams <Name,Value,...>  [default: auto]
     582         A comma delimited list of parameter name and value pairs for writing
     583         molecules to files. The supported parameter names for different file
     584         formats, along with their default values, are shown below:
     585
     586             SD: compute2DCoords,auto,kekulize,no
     587
     588         Default value for compute2DCoords: yes for SMILES input file; no for all other
     589         file types.
     590     -p, --precision <number>  [default: 3]
     591         Floating point precision for writing the calculated descriptor values.
     592     -s, --smilesOut <yes or no>  [default: no]
     593         Write out SMILES string to CSV/TSV text output file.
     594     --overwrite
     595         Overwrite existing files.
     596     -w, --workingdir <dir>
     597         Location of working directory which defaults to the current directory.
     598
     599 Examples:
     600     To compute all available 2D descriptors except Autocorr2D descriptor and
     601     write out a CSV file, type:
     602
     603         % RDKitCalculateMolecularDescriptors.py  -i Sample.smi -o SampleOut.csv
     604
     605     To compute all available 2D descriptors including Autocorr2D descriptor and
     606     excluding fragment count descriptors, and write out a TSV file, type:
     607
     608         % RDKitCalculateMolecularDescriptors.py  -m 2D -a no -f no
     609           -i Sample.smi -o SampleOut.tsv
     610
     611     To compute all available 3D descriptors and write out a SD file, type:
     612
     613         % RDKitCalculateMolecularDescriptors.py  -m 3D -i Sample3D.sdf
     614           -o Sample3DOut.sdf
     615
     616     To compute only fragment count 2D descriptors and write out a SD
     617     file file, type:
     618
     619         % RDKitCalculateMolecularDescriptors.py  -m FragmentCountOnly
     620           -i Sample.sdf -o SampleOut.sdf
     621
     622     To compute all available 2D and 3D descriptors including fragment count and
     623     Autocorr2D and write out a CSV file, type:
     624
     625         % RDKitCalculateMolecularDescriptors.py  -m All -a no -i Sample.sdf
     626           -o SampleOut.csv
     627
     628     To compute a specific set of 2D and 3D descriptors and write out a
     629     write out a TSV file, type:
     630
     631         % RDKitCalculateMolecularDescriptors.py  -m specify
     632           -d 'MolWt,MolLogP,NHOHCount, NOCount,RadiusOfGyration'
     633           -i Sample3D.sdf -o SampleOut.csv
     634
     635     To compute all available 2D descriptors except Autocorr2D descriptor for
     636     molecules in a CSV SMILES file, SMILES strings in column 1, name in
     637     column 2, and write out a SD file without calculation of 2D coordinates, type:
     638
     639         % RDKitCalculateMolecularDescriptors.py --infileParams
     640           "smilesDelimiter,comma,smilesTitleLine,yes,smilesColumn,1,
     641           smilesNameColumn,2" --outfileParams "compute2DCoords,no"
     642           -i SampleSMILES.csv -o SampleOut.sdf
     643
     644 Author:
     645     Manish Sud(msud@san.rr.com)
     646
     647 See also:
     648     RDKitCalculateRMSD.py, RDKitCompareMoleculeShapes.py, RDKitConvertFileFormat.py,
     649     RDKitGenerateConformers.py, RDKitPerformMinimization.py
     650
     651 Copyright:
     652     Copyright (C) 2018 Manish Sud. All rights reserved.
     653
     654     The functionality available in this script is implemented using RDKit, an
     655     open source toolkit for cheminformatics developed by Greg Landrum.
     656
     657     This file is part of MayaChemTools.
     658
     659     MayaChemTools is free software; you can redistribute it and/or modify it under
     660     the terms of the GNU Lesser General Public License as published by the Free
     661     Software Foundation; either version 3 of the License, or (at your option) any
     662     later version.
     663
     664 """
     665
     666 if __name__ == "__main__":
     667     main()