       1 #!/bin/env python
       2 #
       3 # File: RDKitGenerateConformers.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 Descriptors
      43 except ImportError as ErrMsg:
      44     sys.stderr.write("\nFailed to import RDKit module/package: %s\n" % ErrMsg)
      45     sys.stderr.write("Check/update your RDKit environment and try again.\n\n")
      46     sys.exit(1)
      47
      48 # MayaChemTools imports...
      49 try:
      50     from docopt import docopt
      51     import MiscUtil
      52     import RDKitUtil
      53 except ImportError as ErrMsg:
      54     sys.stderr.write("\nFailed to import MayaChemTools module/package: %s\n" % ErrMsg)
      55     sys.stderr.write("Check/update your MayaChemTools environment and try again.\n\n")
      56     sys.exit(1)
      57
      58 ScriptName = os.path.basename(sys.argv[0])
      59 Options = {}
      60 OptionsInfo = {}
      61
      62 def main():
      63     """Start execution of the script"""
      64
      65     MiscUtil.PrintInfo("\n%s (RDK v%s; %s): Starting...\n" % (ScriptName, rdBase.rdkitVersion, time.asctime()))
      66
      67     (WallClockTime, ProcessorTime) = MiscUtil.GetWallClockAndProcessorTime()
      68
      69     # Retrieve command line arguments and options...
      70     RetrieveOptions()
      71
      72     # Process and validate command line arguments and options...
      73     ProcessOptions()
      74
      75     # Perform actions required by the script...
      76     GenerateConformers()
      77
      78     MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName)
      79     MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime))
      80
      81 def GenerateConformers():
      82     """Generate conformers."""
      83
      84     Infile = OptionsInfo["Infile"]
      85     Outfile = OptionsInfo["Outfile"]
      86
      87     # Setup a molecule reader...
      88     MiscUtil.PrintInfo("\nProcessing file %s..." % Infile)
      89     Mols  = RDKitUtil.ReadMolecules(Infile, **OptionsInfo["InfileParams"])
      90
      91     # Set up a molecule writer...
      92     Writer = None
      93     Writer = RDKitUtil.MoleculesWriter(Outfile, **OptionsInfo["OutfileParams"])
      94     if Writer is None:
      95         MiscUtil.PrintError("Failed to setup a writer for output fie %s " % Outfile)
      96     MiscUtil.PrintInfo("Generating file %s...\n" % Outfile)
      97
      98     # Process molecules...
      99     MolCount = 0
     100     ValidMolCount = 0
     101     ConfGenFailedCount = 0
     102
     103     SkipMinimization = OptionsInfo["SkipForceFieldMinimization"]
     104
     105     if SkipMinimization:
     106         MiscUtil.PrintInfo("Generating conformers without performing energy minimization...\n")
     107     else:
     108         MiscUtil.PrintInfo("Generating conformers and performing energy minimization...\n")
     109
     110     for Mol in Mols:
     111         MolCount += 1
     112
     113         if Mol is None:
     114             continue
     115
     116         if RDKitUtil.IsMolEmpty(Mol):
     117             MolName = RDKitUtil.GetMolName(Mol, MolCount)
     118             MiscUtil.PrintWarning("Ignoring empty molecule: %s" % MolName)
     119             continue
     120
     121         ValidMolCount += 1
     122
     123         if SkipMinimization:
     124             Status = GenerateMolConformers(Mol, MolCount, Writer)
     125         else:
     126             Status = GenerateAndMinimizeMolConformers(Mol, MolCount, Writer)
     127
     128         if not Status:
     129             ConfGenFailedCount += 1
     130
     131     if Writer is not None:
     132         Writer.close()
     133
     134     MiscUtil.PrintInfo("\nTotal number of molecules: %d" % MolCount)
     135     MiscUtil.PrintInfo("Number of valid molecules: %d" % ValidMolCount)
     136     MiscUtil.PrintInfo("Number of molecules failed during conformation generation or minimization: %d" % ConfGenFailedCount)
     137     MiscUtil.PrintInfo("Number of ignored molecules: %d" % (MolCount - ValidMolCount + ConfGenFailedCount))
     138
     139 def GenerateMolConformers(Mol, MolCount, Writer):
     140     "Generate conformers for a molecule and write them out."
     141
     142     MolName = RDKitUtil.GetMolName(Mol, MolCount)
     143
     144     ConfIDs = EmbedMolecule(Mol)
     145     if not len(ConfIDs):
     146         MiscUtil.PrintWarning("Conformation generation couldn't be performed for molecule %s: Embedding failed...\n" % MolName)
     147         return False
     148
     149     if OptionsInfo["AlignConformers"]:
     150         AllChem.AlignMolConformers(Mol)
     151
     152
     153     # Write out the conformers...
     154     for ConfID in ConfIDs:
     155         SetConfMolName(Mol, MolName, ConfID)
     156         Writer.write(Mol, confId = ConfID)
     157
     158     MiscUtil.PrintInfo("\nNumber of conformations written for %s: %d" % (MolName, len(ConfIDs)))
     159
     160     return True
     161
     162 def GenerateAndMinimizeMolConformers(Mol, MolCount, Writer):
     163     "Generate and mininize conformers for a molecule and write them out."
     164
     165     if  OptionsInfo["AddHydrogens"]:
     166         Mol = Chem.AddHs(Mol)
     167
     168     ConfIDs = EmbedMolecule(Mol)
     169     if not len(ConfIDs):
     170         MolName = RDKitUtil.GetMolName(Mol, MolCount)
     171         MiscUtil.PrintWarning("Conformation generation couldn't be performed for molecule %s: Embedding failed...\n" % MolName)
     172         return False
     173
     174     CalcEnergyMap = {}
     175     for ConfID in ConfIDs:
     176         try:
     177             if OptionsInfo["UseUFF"]:
     178                 Status = AllChem.UFFOptimizeMolecule(Mol, confId = ConfID, maxIters = OptionsInfo["MaxIters"])
     179             elif OptionsInfo["UseMMFF"]:
     180                 Status = AllChem.MMFFOptimizeMolecule(Mol, confId = ConfID, maxIters = OptionsInfo["MaxIters"])
     181             else:
     182                 MiscUtil.PrintError("Minimization couldn't be performed: Specified forcefield, %s, is not supported" % OptionsInfo["ForceField"])
     183         except RuntimeError as ErrMsg:
     184             MolName = RDKitUtil.GetMolName(Mol, MolCount)
     185             MiscUtil.PrintWarning("Minimization couldn't be performed for molecule %s:\n%s\n" % (MolName, ErrMsg))
     186             return False
     187
     188         EnergyStatus, Energy = GetConformerEnergy(Mol, ConfID)
     189         if not EnergyStatus:
     190             MolName = RDKitUtil.GetMolName(Mol, MolCount)
     191             MiscUtil.PrintWarning("Failed to retrieve calculated energy for conformation number %d of molecule %s. Try again after removing any salts or cleaing up the molecule...\n" % (ConfID, MolName))
     192             return False
     193
     194         if Status != 0:
     195             MolName = RDKitUtil.GetMolName(Mol, MolCount)
     196             MiscUtil.PrintWarning("Minimization failed to converge for conformation number %d of molecule %s in %d steps. Try using higher value for \"--maxIters\" option...\n" % (ConfID, MolName, OptionsInfo["MaxIters"]))
     197
     198         CalcEnergyMap[ConfID] = Energy
     199
     200     if  OptionsInfo["RemoveHydrogens"]:
     201         Mol = Chem.RemoveHs(Mol)
     202
     203     # Align molecules after minimization...
     204     if OptionsInfo["AlignConformers"]:
     205         AllChem.AlignMolConformers(Mol)
     206
     207     SortedConfIDs = sorted(ConfIDs, key = lambda ConfID: CalcEnergyMap[ConfID])
     208
     209     MinEnergyConfID = SortedConfIDs[0]
     210     MinConfEnergy = CalcEnergyMap[MinEnergyConfID]
     211
     212     EnergyWindow = OptionsInfo["EnergyWindow"]
     213     MolName = RDKitUtil.GetMolName(Mol, MolCount)
     214
     215     EnergyRMSDCutoff = OptionsInfo["EnergyRMSDCutoff"]
     216     ApplyEnergyRMSDCutoff = False
     217     if EnergyRMSDCutoff > 0:
     218         ApplyEnergyRMSDCutoff = True
     219
     220     # Calculate RMSD values for conformers...
     221     PreAligned = False
     222     if OptionsInfo["AlignConformers"]:
     223         PreAligned = True
     224
     225     CalcRMSDMap = {}
     226     if ApplyEnergyRMSDCutoff:
     227         for ConfID in SortedConfIDs:
     228             RMSD = AllChem.GetConformerRMS(Mol, MinEnergyConfID, ConfID, prealigned=PreAligned)
     229             CalcRMSDMap[ConfID] = RMSD
     230
     231     # Write out the conformers with in the specified energy window  from the lowest
     232     # energy conformation along with applying RMSD cutoff as needed...
     233     #
     234     ConfCount = 0
     235     IgnoredByEnergyConfCount = 0
     236     IgnoredByRMSDConfCount = 0
     237
     238     FirstConf = True
     239
     240     for ConfID in SortedConfIDs:
     241         if FirstConf:
     242             FirstConf = False
     243             # Write it out...
     244             ConfCount += 1
     245             SetConfMolName(Mol, MolName, ConfID)
     246             Writer.write(Mol, confId = ConfID)
     247             continue
     248
     249         ConfEnergyDiff = abs(CalcEnergyMap[ConfID] - MinConfEnergy )
     250         if  ConfEnergyDiff > EnergyWindow:
     251             IgnoredByEnergyConfCount += 1
     252             continue
     253
     254         if ApplyEnergyRMSDCutoff:
     255             if CalcRMSDMap[ConfID] < EnergyRMSDCutoff:
     256                 IgnoredByRMSDConfCount += 1
     257                 continue
     258
     259         # Write it out...
     260         ConfCount += 1
     261         SetConfMolName(Mol, MolName, ConfID)
     262         Writer.write(Mol, confId = ConfID)
     263
     264     MiscUtil.PrintInfo("\nTotal Number of conformations written for %s: %d" % (MolName, ConfCount))
     265     MiscUtil.PrintInfo("Number of conformations ignored due to energy window cutoff: %d" % (IgnoredByEnergyConfCount))
     266     if ApplyEnergyRMSDCutoff:
     267         MiscUtil.PrintInfo("Number of conformations ignored due to energy RMSD cutoff:  %d" % (IgnoredByRMSDConfCount))
     268
     269     return True
     270
     271 def GetConformerEnergy(Mol, ConfID):
     272     "Calculate conformer energy"
     273
     274     Status = True
     275     Energy = 0.0
     276
     277     if OptionsInfo["UseUFF"]:
     278         UFFMoleculeForcefield = AllChem.UFFGetMoleculeForceField(Mol, confId = ConfID)
     279         if UFFMoleculeForcefield is None:
     280             Status = False
     281         else:
     282             Energy = UFFMoleculeForcefield.CalcEnergy()
     283     elif OptionsInfo["UseMMFF"]:
     284         MMFFMoleculeProperties = AllChem.MMFFGetMoleculeProperties(Mol)
     285         MMFFMoleculeForcefield = AllChem.MMFFGetMoleculeForceField(Mol, MMFFMoleculeProperties, confId = ConfID)
     286         if MMFFMoleculeForcefield is None:
     287             Status = False
     288         else:
     289             Energy = MMFFMoleculeForcefield.CalcEnergy()
     290     else:
     291         MiscUtil.PrintError("Couldn't retrieve conformer energy: Specified forcefield, %s, is not supported" % OptionsInfo["ForceField"])
     292
     293     return (Status, Energy)
     294
     295 def EmbedMolecule(Mol):
     296     "Embed conformations"
     297
     298     ConfIDs = []
     299
     300     # Figure out the number of conformations to embded...
     301     if re.match("^Auto$", OptionsInfo["MaxConfs"], re.I):
     302         NumOfRotBonds = Descriptors.NumRotatableBonds(Mol)
     303         if NumOfRotBonds <= 5:
     304             MaxConfs = 100
     305         elif NumOfRotBonds >=6 and NumOfRotBonds <= 10:
     306             MaxConfs = 200
     307         else:
     308             MaxConfs = 300
     309     else:
     310         MaxConfs = int(OptionsInfo["MaxConfs"])
     311
     312     RandomSeed = OptionsInfo["RandomSeed"]
     313     EnforceChirality = OptionsInfo["EnforceChirality"]
     314     UseExpTorsionAnglePrefs = OptionsInfo["UseExpTorsionAnglePrefs"]
     315     UseBasicKnowledge = OptionsInfo["UseBasicKnowledge"]
     316     EmbedRMSDCutoff = OptionsInfo["EmbedRMSDCutoff"]
     317
     318     ConfIDs = AllChem.EmbedMultipleConfs(Mol, numConfs = MaxConfs, randomSeed = RandomSeed, pruneRmsThresh = EmbedRMSDCutoff, enforceChirality = EnforceChirality, useExpTorsionAnglePrefs = UseExpTorsionAnglePrefs, useBasicKnowledge = UseBasicKnowledge)
     319
     320     return ConfIDs
     321
     322 def SetConfMolName(Mol, MolName, ConfCount):
     323     """Set conf mol name"""
     324
     325     ConfName = "%s_Conf%d" % (MolName, ConfCount)
     326     Mol.SetProp("_Name", ConfName)
     327
     328 def ProcessOptions():
     329     """Process and validate command line arguments and options"""
     330
     331     MiscUtil.PrintInfo("Processing options...")
     332
     333     # Validate options...
     334     ValidateOptions()
     335
     336     OptionsInfo["Infile"] = Options["--infile"]
     337     OptionsInfo["InfileParams"] = MiscUtil.ProcessOptionInfileParameters("--infileParams", Options["--infileParams"], Options["--infile"])
     338
     339     OptionsInfo["Outfile"] = Options["--outfile"]
     340     OptionsInfo["OutfileParams"] = MiscUtil.ProcessOptionOutfileParameters("--outfileParams", Options["--outfileParams"])
     341
     342     OptionsInfo["Overwrite"] = Options["--overwrite"]
     343
     344     OptionsInfo["AddHydrogens"] = True
     345     if re.match("^no$", Options["--addHydrogens"], re.I):
     346         OptionsInfo["AddHydrogens"] = False
     347
     348     OptionsInfo["AlignConformers"] = True
     349     if re.match("^no$", Options["--alignConformers"], re.I):
     350         OptionsInfo["AlignConformers"] = False
     351
     352     if re.match("^ETDG$", Options["--conformerGenerator"], re.I):
     353         ConformerGenerator = "ETDG"
     354         UseExpTorsionAnglePrefs = True
     355         UseBasicKnowledge = False
     356     elif re.match("^KDG$", Options["--conformerGenerator"], re.I):
     357         ConformerGenerator = "KDG"
     358         UseExpTorsionAnglePrefs = False
     359         UseBasicKnowledge = True
     360     elif re.match("^ETKDG$", Options["--conformerGenerator"], re.I):
     361         ConformerGenerator = "ETKDG"
     362         UseExpTorsionAnglePrefs = True
     363         UseBasicKnowledge = True
     364     elif re.match("^SDG$", Options["--conformerGenerator"], re.I):
     365         ConformerGenerator = "SDG"
     366         UseExpTorsionAnglePrefs = False
     367         UseBasicKnowledge = False
     368
     369     OptionsInfo["ConformerGenerator"] = ConformerGenerator
     370     OptionsInfo["UseExpTorsionAnglePrefs"] = UseExpTorsionAnglePrefs
     371     OptionsInfo["UseBasicKnowledge"] = UseBasicKnowledge
     372
     373     if re.match("^UFF$", Options["--forceField"], re.I):
     374         ForceField = "UFF"
     375         UseUFF = True
     376         UseMMFF = False
     377         SkipForceFieldMinimization = False
     378     elif re.match("^MMFF$", Options["--forceField"], re.I):
     379         ForceField = "MMFF"
     380         UseUFF = False
     381         UseMMFF = True
     382         SkipForceFieldMinimization = False
     383     else:
     384         ForceField = "None"
     385         UseUFF = False
     386         UseMMFF = False
     387         SkipForceFieldMinimization = True
     388
     389     OptionsInfo["SkipForceFieldMinimization"] = SkipForceFieldMinimization
     390     OptionsInfo["ForceField"] = ForceField
     391     OptionsInfo["UseMMFF"] = UseMMFF
     392     OptionsInfo["UseUFF"] = UseUFF
     393
     394     OptionsInfo["EnforceChirality"] = True
     395     if re.match("^no$", Options["--enforceChirality"], re.I):
     396         OptionsInfo["EnforceChirality"] = False
     397
     398     OptionsInfo["EnergyWindow"] = float(Options["--energyWindow"])
     399
     400     EmbedRMSDCutoff = -1.0
     401     if not re.match("^none$", Options["--embedRMSDCutoff"], re.I):
     402         EmbedRMSDCutoff = float(Options["--embedRMSDCutoff"])
     403     OptionsInfo["EmbedRMSDCutoff"] = EmbedRMSDCutoff
     404
     405     EnergyRMSDCutoff = -1.0
     406     if not re.match("^none$", Options["--energyRMSDCutoff"], re.I):
     407         EnergyRMSDCutoff = float(Options["--energyRMSDCutoff"])
     408     OptionsInfo["EnergyRMSDCutoff"] = EnergyRMSDCutoff
     409
     410     OptionsInfo["MaxIters"] = int(Options["--maxIters"])
     411     OptionsInfo["MaxConfs"] = Options["--maxConfs"]
     412
     413     RandomSeed = -1
     414     if not re.match("^auto$", Options["--randomSeed"], re.I):
     415         RandomSeed = int(Options["--randomSeed"])
     416     OptionsInfo["RandomSeed"] = RandomSeed
     417
     418     OptionsInfo["RemoveHydrogens"] = True
     419     if re.match("^no$", Options["--removeHydrogens"], re.I):
     420         OptionsInfo["RemoveHydrogens"] = False
     421
     422 def RetrieveOptions():
     423     """Retrieve command line arguments and options"""
     424
     425     # Get options...
     426     global Options
     427     Options = docopt(_docoptUsage_)
     428
     429     # Set current working directory to the specified directory...
     430     WorkingDir = Options["--workingdir"]
     431     if WorkingDir:
     432         os.chdir(WorkingDir)
     433
     434     # Handle examples option...
     435     if "--examples" in Options and Options["--examples"]:
     436         MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_))
     437         sys.exit(0)
     438
     439 def ValidateOptions():
     440     """Validate option values"""
     441
     442     MiscUtil.ValidateOptionTextValue("-a, --addHydrogens", Options["--addHydrogens"], "yes no")
     443     MiscUtil.ValidateOptionTextValue("--alignConformers", Options["--alignConformers"], "yes no")
     444     MiscUtil.ValidateOptionTextValue("-c, --conformerGenerator", Options["--conformerGenerator"], "SDG ETDG KDG ETKDG")
     445     MiscUtil.ValidateOptionTextValue("-f, --forceField", Options["--forceField"], "UFF MMFF None")
     446
     447     MiscUtil.ValidateOptionTextValue("--enforceChirality ", Options["--enforceChirality"], "yes no")
     448     MiscUtil.ValidateOptionFloatValue("--energyWindow", Options["--energyWindow"], {">": 0.0})
     449
     450     if not re.match("^none$", Options["--embedRMSDCutoff"], re.I):
     451         MiscUtil.ValidateOptionFloatValue("--embedRMSDCutoff", Options["--embedRMSDCutoff"], {">": 0.0})
     452
     453     if not re.match("^none$", Options["--energyRMSDCutoff"], re.I):
     454         MiscUtil.ValidateOptionFloatValue("--energyRMSDCutoff", Options["--energyRMSDCutoff"], {">": 0.0})
     455         # Make sure that the alignConformers option is being used...
     456         if not re.match("^yes$", Options["--alignConformers"], re.I):
     457             MiscUtil.PrintError("%s value of \"--alignConformers\" is not allowed for %s value of \"--energyRMSDCutoff\" option " % (Options["--alignConformers"], Options["--energyRMSDCutoff"]))
     458
     459     MiscUtil.ValidateOptionFilePath("-i, --infile", Options["--infile"])
     460     MiscUtil.ValidateOptionFileExt("-i, --infile", Options["--infile"], "sdf sd mol smi txt csv tsv")
     461
     462     MiscUtil.ValidateOptionFileExt("-o, --outfile", Options["--outfile"], "sdf sd")
     463     MiscUtil.ValidateOptionsOutputFileOverwrite("-o, --outfile", Options["--outfile"], "--overwrite", Options["--overwrite"])
     464     MiscUtil.ValidateOptionsDistinctFileNames("-i, --infile", Options["--infile"], "-o, --outfile", Options["--outfile"])
     465
     466     if not re.match("^auto$", Options["--maxConfs"], re.I):
     467         MiscUtil.ValidateOptionIntegerValue("--maxConfs", Options["--maxConfs"], {">": 0})
     468
     469     MiscUtil.ValidateOptionIntegerValue("--maxIters", Options["--maxIters"], {">": 0})
     470
     471     if not re.match("^auto$", Options["--randomSeed"], re.I):
     472         MiscUtil.ValidateOptionIntegerValue("--randomSeed", Options["--randomSeed"], {})
     473         RandomSeed = int(Options["--randomSeed"])
     474
     475     MiscUtil.ValidateOptionTextValue("-r, --removeHydrogens", Options["--removeHydrogens"], "yes no")
     476
     477 # Setup a usage string for docopt...
     478 _docoptUsage_ = """
     479 RDKitGenerateConformers.py - Generate molecular conformations
     480
     481 Usage:
     482     RDKitGenerateConformers.py [--alignConformers <yes or no>] [--addHydrogens <yes or no>]
     483                                [--conformerGenerator <SDG, ETDG, KDG, ETKDG>] [--embedRMSDCutoff <number>]
     484                                [--enforceChirality <yes or no>] [--energyRMSDCutoff <number>]
     485                                [--energyWindow <number> ]  [--forceField <UFF, MMFF, None>]
     486                                [--infileParams <Name,Value,...>] [--maxConfs <number>]
     487                                [--maxIters <number>]  [ --outfileParams <Name,Value,...> ]  [--overwrite]
     488                                [ --removeHydrogens <yes or no>] [--randomSeed <number>] [-w <dir>] -i <infile> -o <outfile>
     489     RDKitGenerateConformers.py -h | --help | -e | --examples
     490
     491 Description:
     492     Generate molecular conformations using a combination of distance geometry and
     493     forcefield minimization. The forcefield minimization may be skipped to only generate
     494     conformations by available distance geometry based methodologies.
     495
     496     The supported input file formats are: Mol (.mol), SD (.sdf, .sd), SMILES (.smi,
     497     .csv, .tcsv .txt)
     498
     499     The supported output file format are: SD (.sdf, .sd)
     500
     501 Options:
     502     -a, --addHydrogens <yes or no>  [default: yes]
     503         Add hydrogens before minimization.
     504     --alignConformers <yes or no>  [default: yes]
     505         Align conformers for each molecule.
     506     -c, --conformerGenerator <SDG, ETDG, KDG, ETKDG>  [default: ETKDG]
     507         Conformation generation methodology for generating initial 3D coordinates of a
     508         molecule. Possible values: Standard Distance Geometry, (SDG), Experimental
     509         Torsion-angle preference with Distance Geometry (ETDG)  [Ref 129] , basic Knowledge-terms
     510         with Distance Geometry (KDG), and Experimental Torsion-angle preference along
     511         with basic Knowledge-terms and Distance Geometry (ETKDG).
     512     --embedRMSDCutoff <number>  [default: none]
     513         RMSD cutoff for retaining conformations after embedding and before energy minimization.
     514         All embedded conformations are kept by default. Otherwise, only those conformations
     515         which are different from each other by the specified RMSD cutoff are kept. The first
     516         embedded conformation is always retained.
     517     --enforceChirality <yes or no>  [default: Yes]
     518         Enforce chirality for defined chiral centers.
     519     --energyRMSDCutoff <number>  [default: none]
     520         RMSD cutoff for retaining conformations after energy minimization. By default,
     521         all minimized conformations with in the specified energy window from the lowest energy
     522         conformation are kept. Otherwise, only those conformations which are different from
     523         the lowest energy conformation by the specified RMSD cutoff and are with in the
     524         specified energy window are kept. The lowest energy conformation is always retained.
     525     --energyWindow <number>  [default: 20]
     526         Energy window in kcal/mol for selecting conformers. This option is ignored during
     527         'None' value of '-f, --forcefield' option.
     528     -e, --examples
     529         Print examples.
     530     -f, --forceField <UFF, MMFF, None>  [default: MMFF]
     531         Forcefield method to use for energy minimization. Possible values: Universal Force
     532         Field (UFF) [Ref 81],  Merck Molecular Mechanics Force Field (MMFF) [Ref 83-87] or
     533         None.
     534     -h, --help
     535         Print this help message.
     536     -i, --infile <infile>
     537         Input file name.
     538     --infileParams <Name,Value,...>  [default: auto]
     539         A comma delimited list of parameter name and value pairs for reading
     540         molecules from files. The supported parameter names for different file
     541         formats, along with their default values, are shown below:
     542
     543             SD, MOL: removeHydrogens,yes,sanitize,yes,strictParsing,yes
     544             SMILES: smilesColumn,1,smilesNameColumn,2,smilesDelimiter,space,
     545                 smilesTitleLine,auto,sanitize,yes
     546
     547         Possible values for smilesDelimiter: space, comma or tab.
     548     --maxConfs <number>  [default: auto]
     549         Maximum number of conformations to generate for each molecule by conformation
     550         generation methodology. The conformations are minimized using the specified
     551         forcefield as needed and written to the output file. The default value for maximum
     552         number of conformations is dependent on the number of rotatable bonds in molecules:
     553         RotBonds <= 5, maxConfs = 100; RotBonds >=6 and <= 10, MaxConfs = 200;
     554         RotBonds >= 11, maxConfs = 300
     555     --maxIters <number>  [default: 250]
     556         Maximum number of iterations to perform for each molecule during forcefield
     557         minimization.
     558     -o, --outfile <outfile>
     559         Output file name.
     560     --outfileParams <Name,Value,...>  [default: auto]
     561         A comma delimited list of parameter name and value pairs for writing
     562         molecules to files. The supported parameter names for different file
     563         formats, along with their default values, are shown below:
     564
     565             SD: kekulize,no
     566
     567     --overwrite
     568         Overwrite existing files.
     569     -r, --removeHydrogens <yes or no>  [default: Yes]
     570         Remove hydrogens after minimization.
     571     --randomSeed <number>  [default: auto]
     572         Seed for the random number generator for reproducing 3D coordinates.
     573         Default is to use a random seed.
     574     -w, --workingdir <dir>
     575         Location of working directory which defaults to the current directory.
     576
     577 Examples:
     578     To generate conformers using Experimental Torsion-angle preference along
     579     with basic Knowledge-terms and Distance Geometry (ETKDG) followed by
     580     MMFF minimization with automatic determination of maximum number of
     581     conformers for each molecule and write out a SD file, type:
     582
     583         % RDKitGenerateConformers.py  -i Sample.smi -o SampleOut.sdf
     584
     585     To generate up to 150 conformers for each molecule using ETKDG and UFF forcefield
     586     minimization along with conformers within 25 kcal/mol energy window and write out a
     587     SD file, type:
     588
     589         % RDKitGenerateConformers.py  --energyWindow 25 -f UFF --maxConfs 150
     590           -i Sample.smi -o SampleOut.sdf
     591
     592     To generate up to 50 conformers for each molecule using KDG without any forcefield
     593     minimization and alignment of conformers and write out a SD file, type:
     594
     595         % RDKitGenerateConformers.py  -f none --maxConfs 50 --alignConformers no
     596           -i Sample.sdf -o SampleOut.sdf
     597
     598     To generate up to 50 conformers using SDG without any forcefield minimization
     599     and alignment of conformers for molecules in a  CSV SMILES file, SMILES strings
     600     in column 1, name in column 2, and write out a SD file, type:
     601
     602         % RDKitGenerateConformers.py  --maxConfs 50  --maxIters 50 -c SDG
     603           --alignConformers no -f none --infileParams "smilesDelimiter,comma,
     604           smilesTitleLine,yes, smilesColumn,1,smilesNameColumn,2"
     605           -i SampleSMILES.csv -o SampleOut.sdf
     606
     607 Author:
     608     Manish Sud(msud@san.rr.com)
     609
     610 See also:
     611     RDKitCalculateRMSD.py, RDKitCalculateMolecularDescriptors.py,
     612     RDKitCompareMoleculeShapes.py, RDKitConvertFileFormat.py,
     613     RDKitPerformMinimization.py
     614
     615 Copyright:
     616     Copyright (C) 2018 Manish Sud. All rights reserved.
     617
     618     The functionality available in this script is implemented using RDKit, an
     619     open source toolkit for cheminformatics developed by Greg Landrum.
     620
     621     This file is part of MayaChemTools.
     622
     623     MayaChemTools is free software; you can redistribute it and/or modify it under
     624     the terms of the GNU Lesser General Public License as published by the Free
     625     Software Foundation; either version 3 of the License, or (at your option) any
     626     later version.
     627
     628 """
     629
     630 if __name__ == "__main__":
     631     main()