1 #!/bin/env python 2 # 3 # File: RDKitSearchSMARTS.py 4 # Author: Manish Sud <msud@san.rr.com> 5 # 6 # Copyright (C) 2026 Manish Sud. All rights reserved. 7 # 8 # The functionality available in this script is implemented using 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 import os 32 import sys 33 import time 34 import re 35 import multiprocessing as mp 36 37 # RDKit imports... 38 try: 39 from rdkit import rdBase 40 from rdkit import Chem 41 from rdkit.Chem import AllChem 42 except ImportError as ErrMsg: 43 sys.stderr.write("\nFailed to import RDKit module/package: %s\n" % ErrMsg) 44 sys.stderr.write("Check/update your RDKit environment and try again.\n\n") 45 sys.exit(1) 46 47 # MayaChemTools imports... 48 sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), "..", "lib", "Python")) 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 63 def main(): 64 """Start execution of the script.""" 65 66 MiscUtil.PrintInfo( 67 "\n%s (RDKit v%s; MayaChemTools v%s; %s): Starting...\n" 68 % (ScriptName, rdBase.rdkitVersion, MiscUtil.GetMayaChemToolsVersion(), time.asctime()) 69 ) 70 71 (WallClockTime, ProcessorTime) = MiscUtil.GetWallClockAndProcessorTime() 72 73 # Retrieve command line arguments and options... 74 RetrieveOptions() 75 76 # Process and validate command line arguments and options... 77 ProcessOptions() 78 79 # Perform actions required by the script... 80 PerformSearch() 81 82 MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName) 83 MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime)) 84 85 86 def PerformSearch(): 87 """Perform search using specified SMARTS pattern.""" 88 89 # Set up a pattern molecule... 90 PatternMol = Chem.MolFromSmarts(OptionsInfo["Pattern"]) 91 92 # Setup a molecule reader... 93 MiscUtil.PrintInfo("\nProcessing file %s..." % OptionsInfo["Infile"]) 94 Mols = RDKitUtil.ReadMolecules(OptionsInfo["Infile"], **OptionsInfo["InfileParams"]) 95 96 # Set up molecule writers... 97 Writer, WriterFiltered = SetupMoleculeWriters() 98 99 MolCount, ValidMolCount, RemainingMolCount = ProcessMolecules(Mols, PatternMol, Writer, WriterFiltered) 100 101 if Writer is not None: 102 Writer.close() 103 if WriterFiltered is not None: 104 WriterFiltered.close() 105 106 MiscUtil.PrintInfo("\nTotal number of molecules: %d" % MolCount) 107 MiscUtil.PrintInfo("Number of valid molecules: %d" % ValidMolCount) 108 MiscUtil.PrintInfo("Number of ignored molecules: %d" % (MolCount - ValidMolCount)) 109 110 MiscUtil.PrintInfo("\nNumber of remaining molecules: %d" % RemainingMolCount) 111 MiscUtil.PrintInfo("Number of filtered molecules: %d" % (ValidMolCount - RemainingMolCount)) 112 113 114 def ProcessMolecules(Mols, PatternMol, Writer, WriterFiltered): 115 """Process and filter molecules.""" 116 117 if OptionsInfo["MPMode"]: 118 return ProcessMoleculesUsingMultipleProcesses(Mols, PatternMol, Writer, WriterFiltered) 119 else: 120 return ProcessMoleculesUsingSingleProcess(Mols, PatternMol, Writer, WriterFiltered) 121 122 123 def ProcessMoleculesUsingSingleProcess(Mols, PatternMol, Writer, WriterFiltered): 124 """Process and filter molecules using a single process.""" 125 126 NegateMatch = OptionsInfo["NegateMatch"] 127 OutfileFilteredMode = OptionsInfo["OutfileFilteredMode"] 128 Compute2DCoords = OptionsInfo["OutfileParams"]["Compute2DCoords"] 129 SetSMILESMolProps = OptionsInfo["OutfileParams"]["SetSMILESMolProps"] 130 131 MiscUtil.PrintInfo("\nFiltering molecules...") 132 133 (MolCount, ValidMolCount, RemainingMolCount) = [0] * 3 134 FirstMol = True 135 for Mol in Mols: 136 MolCount += 1 137 138 if Mol is None: 139 continue 140 141 if RDKitUtil.IsMolEmpty(Mol): 142 MolName = RDKitUtil.GetMolName(Mol, MolCount) 143 MiscUtil.PrintWarning("Ignoring empty molecule: %s" % MolName) 144 continue 145 146 ValidMolCount += 1 147 if FirstMol: 148 FirstMol = False 149 if SetSMILESMolProps: 150 if Writer is not None: 151 RDKitUtil.SetWriterMolProps(Writer, Mol) 152 if WriterFiltered is not None: 153 RDKitUtil.SetWriterMolProps(WriterFiltered, Mol) 154 155 MolMatched = DoesMoleculeContainsPattern(Mol, PatternMol) 156 if MolMatched != NegateMatch: 157 RemainingMolCount += 1 158 WriteMolecule(Writer, Mol, Compute2DCoords) 159 else: 160 if OutfileFilteredMode: 161 WriteMolecule(WriterFiltered, Mol, Compute2DCoords) 162 163 return (MolCount, ValidMolCount, RemainingMolCount) 164 165 166 def ProcessMoleculesUsingMultipleProcesses(Mols, PatternMol, Writer, WriterFiltered): 167 """Process and filter molecules using multiprocessing.""" 168 169 MiscUtil.PrintInfo("\nFiltering molecules using multiprocessing...") 170 171 MPParams = OptionsInfo["MPParams"] 172 NegateMatch = OptionsInfo["NegateMatch"] 173 OutfileFilteredMode = OptionsInfo["OutfileFilteredMode"] 174 Compute2DCoords = OptionsInfo["OutfileParams"]["Compute2DCoords"] 175 SetSMILESMolProps = OptionsInfo["OutfileParams"]["SetSMILESMolProps"] 176 177 # Setup data for initializing a worker process... 178 MiscUtil.PrintInfo("Encoding options info and pattern molecule...") 179 OptionsInfo["EncodedPatternMol"] = RDKitUtil.MolToBase64EncodedMolString(PatternMol) 180 InitializeWorkerProcessArgs = ( 181 MiscUtil.ObjectToBase64EncodedString(Options), 182 MiscUtil.ObjectToBase64EncodedString(OptionsInfo), 183 ) 184 185 # Setup a encoded mols data iterable for a worker process... 186 WorkerProcessDataIterable = RDKitUtil.GenerateBase64EncodedMolStrings(Mols) 187 188 # Setup process pool along with data initialization for each process... 189 MiscUtil.PrintInfo( 190 "\nConfiguring multiprocessing using %s method..." 191 % ("mp.Pool.imap()" if re.match("^Lazy$", MPParams["InputDataMode"], re.I) else "mp.Pool.map()") 192 ) 193 MiscUtil.PrintInfo( 194 "NumProcesses: %s; InputDataMode: %s; ChunkSize: %s\n" 195 % ( 196 MPParams["NumProcesses"], 197 MPParams["InputDataMode"], 198 ("automatic" if MPParams["ChunkSize"] is None else MPParams["ChunkSize"]), 199 ) 200 ) 201 202 ProcessPool = mp.Pool(MPParams["NumProcesses"], InitializeWorkerProcess, InitializeWorkerProcessArgs) 203 204 # Start processing... 205 if re.match("^Lazy$", MPParams["InputDataMode"], re.I): 206 Results = ProcessPool.imap(WorkerProcess, WorkerProcessDataIterable, MPParams["ChunkSize"]) 207 elif re.match("^InMemory$", MPParams["InputDataMode"], re.I): 208 Results = ProcessPool.map(WorkerProcess, WorkerProcessDataIterable, MPParams["ChunkSize"]) 209 else: 210 MiscUtil.PrintError( 211 'The value, %s, specified for "--inputDataMode" is not supported.' % (MPParams["InputDataMode"]) 212 ) 213 214 (MolCount, ValidMolCount, RemainingMolCount) = [0] * 3 215 FirstMol = True 216 for Result in Results: 217 MolCount += 1 218 MolIndex, EncodedMol, MolMatched = Result 219 220 if EncodedMol is None: 221 continue 222 ValidMolCount += 1 223 224 Mol = RDKitUtil.MolFromBase64EncodedMolString(EncodedMol) 225 226 if FirstMol: 227 FirstMol = False 228 if SetSMILESMolProps: 229 if Writer is not None: 230 RDKitUtil.SetWriterMolProps(Writer, Mol) 231 if WriterFiltered is not None: 232 RDKitUtil.SetWriterMolProps(WriterFiltered, Mol) 233 234 if MolMatched != NegateMatch: 235 RemainingMolCount += 1 236 WriteMolecule(Writer, Mol, Compute2DCoords) 237 else: 238 if OutfileFilteredMode: 239 WriteMolecule(WriterFiltered, Mol, Compute2DCoords) 240 241 return (MolCount, ValidMolCount, RemainingMolCount) 242 243 244 def InitializeWorkerProcess(*EncodedArgs): 245 """Initialize data for a worker process.""" 246 247 global Options, OptionsInfo 248 249 MiscUtil.PrintInfo("Starting process (PID: %s)..." % os.getpid()) 250 251 # Decode Options and OptionInfo... 252 Options = MiscUtil.ObjectFromBase64EncodedString(EncodedArgs[0]) 253 OptionsInfo = MiscUtil.ObjectFromBase64EncodedString(EncodedArgs[1]) 254 255 # Decode PatternMol... 256 OptionsInfo["PatternMol"] = RDKitUtil.MolFromBase64EncodedMolString(OptionsInfo["EncodedPatternMol"]) 257 258 259 def WorkerProcess(EncodedMolInfo): 260 """Process data for a worker process.""" 261 262 MolIndex, EncodedMol = EncodedMolInfo 263 264 if EncodedMol is None: 265 return [MolIndex, None, False] 266 267 Mol = RDKitUtil.MolFromBase64EncodedMolString(EncodedMol) 268 if RDKitUtil.IsMolEmpty(Mol): 269 MolName = RDKitUtil.GetMolName(Mol, (MolIndex + 1)) 270 MiscUtil.PrintWarning("Ignoring empty molecule: %s" % MolName) 271 return [MolIndex, None, False] 272 273 MolMatched = DoesMoleculeContainsPattern(Mol, OptionsInfo["PatternMol"]) 274 275 return [MolIndex, EncodedMol, MolMatched] 276 277 278 def WriteMolecule(Writer, Mol, Compute2DCoords): 279 """Write out molecule.""" 280 281 if OptionsInfo["CountMode"]: 282 return 283 284 if Compute2DCoords: 285 AllChem.Compute2DCoords(Mol) 286 287 Writer.write(Mol) 288 289 290 def SetupMoleculeWriters(): 291 """Setup molecule writers.""" 292 293 Writer = None 294 WriterFiltered = None 295 296 if OptionsInfo["CountMode"]: 297 return (Writer, WriterFiltered) 298 299 Writer = RDKitUtil.MoleculesWriter(OptionsInfo["Outfile"], **OptionsInfo["OutfileParams"]) 300 if Writer is None: 301 MiscUtil.PrintError("Failed to setup a writer for output fie %s " % OptionsInfo["Outfile"]) 302 MiscUtil.PrintInfo("Generating file %s..." % OptionsInfo["Outfile"]) 303 304 if OptionsInfo["OutfileFilteredMode"]: 305 WriterFiltered = RDKitUtil.MoleculesWriter(OptionsInfo["OutfileFiltered"], **OptionsInfo["OutfileParams"]) 306 if WriterFiltered is None: 307 MiscUtil.PrintError("Failed to setup a writer for output fie %s " % OptionsInfo["OutfileFiltered"]) 308 MiscUtil.PrintInfo("Generating file %s..." % OptionsInfo["OutfileFiltered"]) 309 310 return (Writer, WriterFiltered) 311 312 313 def DoesMoleculeContainsPattern(Mol, PatternMol): 314 """Check presence of pattern in the molecule.""" 315 316 return True if Mol.HasSubstructMatch(PatternMol, useChirality=OptionsInfo["UseChirality"]) else False 317 318 319 def ProcessOptions(): 320 """Process and validate command line arguments and options.""" 321 322 MiscUtil.PrintInfo("Processing options...") 323 324 # Validate options... 325 ValidateOptions() 326 327 OptionsInfo["Infile"] = Options["--infile"] 328 OptionsInfo["InfileParams"] = MiscUtil.ProcessOptionInfileParameters( 329 "--infileParams", Options["--infileParams"], Options["--infile"] 330 ) 331 332 OptionsInfo["Outfile"] = Options["--outfile"] 333 OptionsInfo["OutfileParams"] = MiscUtil.ProcessOptionOutfileParameters( 334 "--outfileParams", Options["--outfileParams"], Options["--infile"], Options["--outfile"] 335 ) 336 337 OptionsInfo["OutfileFiltered"] = "" 338 if Options["--outfile"]: 339 FileDir, FileName, FileExt = MiscUtil.ParseFileName(Options["--outfile"]) 340 OutfileFiltered = "%s_Filtered.%s" % (FileName, FileExt) 341 OptionsInfo["OutfileFiltered"] = OutfileFiltered 342 OptionsInfo["OutfileFilteredMode"] = True if re.match("^yes$", Options["--outfileFiltered"], re.I) else False 343 344 OptionsInfo["Overwrite"] = Options["--overwrite"] 345 346 OptionsInfo["CountMode"] = True if re.match("^count$", Options["--mode"], re.I) else False 347 OptionsInfo["NegateMatch"] = True if re.match("^yes$", Options["--negate"], re.I) else False 348 349 OptionsInfo["MPMode"] = True if re.match("^yes$", Options["--mp"], re.I) else False 350 OptionsInfo["MPParams"] = MiscUtil.ProcessOptionMultiprocessingParameters("--mpParams", Options["--mpParams"]) 351 352 OptionsInfo["Pattern"] = Options["--pattern"] 353 OptionsInfo["UseChirality"] = True if re.match("^yes$", Options["--useChirality"], re.I) else False 354 355 356 def RetrieveOptions(): 357 """Retrieve command line arguments and options.""" 358 359 # Get options... 360 global Options 361 Options = docopt(_docoptUsage_) 362 363 # Set current working directory to the specified directory... 364 WorkingDir = Options["--workingdir"] 365 if WorkingDir: 366 os.chdir(WorkingDir) 367 368 # Handle examples option... 369 if "--examples" in Options and Options["--examples"]: 370 MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_)) 371 sys.exit(0) 372 373 374 def ValidateOptions(): 375 """Validate option values.""" 376 377 MiscUtil.ValidateOptionFilePath("-i, --infile", Options["--infile"]) 378 MiscUtil.ValidateOptionFileExt("-i, --infile", Options["--infile"], "sdf sd smi txt csv tsv") 379 if Options["--outfile"]: 380 MiscUtil.ValidateOptionFileExt("-o, --outfile", Options["--outfile"], "sdf sd smi") 381 MiscUtil.ValidateOptionsOutputFileOverwrite( 382 "-o, --outfile", Options["--outfile"], "--overwrite", Options["--overwrite"] 383 ) 384 MiscUtil.ValidateOptionsDistinctFileNames( 385 "-i, --infile", Options["--infile"], "-o, --outfile", Options["--outfile"] 386 ) 387 388 MiscUtil.ValidateOptionTextValue("--outfileFiltered", Options["--outfileFiltered"], "yes no") 389 390 MiscUtil.ValidateOptionTextValue("-m, --mode", Options["--mode"], "retrieve count") 391 if re.match("^retrieve$", Options["--mode"], re.I): 392 if not Options["--outfile"]: 393 MiscUtil.PrintError( 394 'The outfile must be specified using "-o, --outfile" during "retrieve" value of "-m, --mode" option' 395 ) 396 397 MiscUtil.ValidateOptionTextValue("--mp", Options["--mp"], "yes no") 398 MiscUtil.ValidateOptionTextValue("-n, --negate", Options["--negate"], "yes no") 399 400 PatternMol = Chem.MolFromSmarts(Options["--pattern"]) 401 if PatternMol is None: 402 MiscUtil.PrintError( 403 'The value specified, %s, using option "-p, --pattern" is not a valid SMARTS: Failed to create pattern molecule' 404 % Options["--pattern"] 405 ) 406 407 MiscUtil.ValidateOptionTextValue("--useChirality", Options["--useChirality"], "yes no") 408 409 410 # Setup a usage string for docopt... 411 _docoptUsage_ = """ 412 RDKitSearchSMARTS.py - Perform a substructure search using SMARTS pattern 413 414 Usage: 415 RDKitSearchSMARTS.py [--infileParams <Name,Value,...>] [--mode <retrieve or count>] 416 [--mp <yes or no>] [--mpParams <Name,Value,...>] [--negate <yes or no>] 417 [--outfileFiltered <yes or no>] [--outfileParams <Name,Value,...>] [--overwrite] 418 [--useChirality <yes or no>] [-w <dir>] [-o <outfile>] -p <SMARTS> -i <infile> 419 RDKitSearchSMARTS.py -h | --help | -e | --examples 420 421 Description: 422 Perform a substructure search in an input file using specified SMARTS pattern and 423 write out the matched molecules to an output file or simply count the number 424 of matches. 425 426 The supported input file formats are: SD (.sdf, .sd), SMILES (.smi., csv, .tsv, .txt) 427 428 The supported output file formats are: SD (.sdf, .sd), SMILES (.smi) 429 430 Options: 431 -e, --examples 432 Print examples. 433 -h, --help 434 Print this help message. 435 -i, --infile <infile> 436 Input file name. 437 --infileParams <Name,Value,...> [default: auto] 438 A comma delimited list of parameter name and value pairs for reading 439 molecules from files. The supported parameter names for different file 440 formats, along with their default values, are shown below: 441 442 SD, MOL: removeHydrogens,yes,sanitize,yes,strictParsing,yes 443 SMILES: smilesColumn,1,smilesNameColumn,2,smilesDelimiter,space, 444 smilesTitleLine,auto,sanitize,yes 445 446 Possible values for smilesDelimiter: space, comma or tab. 447 -m, --mode <retrieve or count> [default: retrieve] 448 Specify whether to retrieve and write out matched molecules to an output 449 file or simply count the number of matches. 450 --mp <yes or no> [default: no] 451 Use multiprocessing. 452 453 By default, input data is retrieved in a lazy manner via mp.Pool.imap() 454 function employing lazy RDKit data iterable. This allows processing of 455 arbitrary large data sets without any additional requirements memory. 456 457 All input data may be optionally loaded into memory by mp.Pool.map() 458 before starting worker processes in a process pool by setting the value 459 of 'inputDataMode' to 'InMemory' in '--mpParams' option. 460 461 A word to the wise: The default 'chunkSize' value of 1 during 'Lazy' input 462 data mode may adversely impact the performance. The '--mpParams' section 463 provides additional information to tune the value of 'chunkSize'. 464 --mpParams <Name,Value,...> [default: auto] 465 A comma delimited list of parameter name and value pairs to configure 466 multiprocessing. 467 468 The supported parameter names along with their default and possible 469 values are shown below: 470 471 chunkSize, auto 472 inputDataMode, Lazy [ Possible values: InMemory or Lazy ] 473 numProcesses, auto [ Default: mp.cpu_count() ] 474 475 These parameters are used by the following functions to configure and 476 control the behavior of multiprocessing: mp.Pool(), mp.Pool.map(), and 477 mp.Pool.imap(). 478 479 The chunkSize determines chunks of input data passed to each worker 480 process in a process pool by mp.Pool.map() and mp.Pool.imap() functions. 481 The default value of chunkSize is dependent on the value of 'inputDataMode'. 482 483 The mp.Pool.map() function, invoked during 'InMemory' input data mode, 484 automatically converts RDKit data iterable into a list, loads all data into 485 memory, and calculates the default chunkSize using the following method 486 as shown in its code: 487 488 chunkSize, extra = divmod(len(dataIterable), len(numProcesses) * 4) 489 if extra: chunkSize += 1 490 491 For example, the default chunkSize will be 7 for a pool of 4 worker processes 492 and 100 data items. 493 494 The mp.Pool.imap() function, invoked during 'Lazy' input data mode, employs 495 'lazy' RDKit data iterable to retrieve data as needed, without loading all the 496 data into memory. Consequently, the size of input data is not known a priori. 497 It's not possible to estimate an optimal value for the chunkSize. The default 498 chunkSize is set to 1. 499 500 The default value for the chunkSize during 'Lazy' data mode may adversely 501 impact the performance due to the overhead associated with exchanging 502 small chunks of data. It is generally a good idea to explicitly set chunkSize to 503 a larger value during 'Lazy' input data mode, based on the size of your input 504 data and number of processes in the process pool. 505 506 The mp.Pool.map() function waits for all worker processes to process all 507 the data and return the results. The mp.Pool.imap() function, however, 508 returns the the results obtained from worker processes as soon as the 509 results become available for specified chunks of data. 510 511 The order of data in the results returned by both mp.Pool.map() and 512 mp.Pool.imap() functions always corresponds to the input data. 513 -n, --negate <yes or no> [default: no] 514 Specify whether to find molecules not matching the specified SMARTS pattern. 515 -o, --outfile <outfile> 516 Output file name. 517 --outfileFiltered <yes or no> [default: no] 518 Write out a file containing filtered molecules. Its name is automatically 519 generated from the specified output file. Default: <OutfileRoot>_ 520 Filtered.<OutfileExt>. 521 --outfileParams <Name,Value,...> [default: auto] 522 A comma delimited list of parameter name and value pairs for writing 523 molecules to files. The supported parameter names for different file 524 formats, along with their default values, are shown below: 525 526 SD: compute2DCoords,auto,kekulize,yes,forceV3000,no 527 SMILES: smilesKekulize,no,smilesDelimiter,space, smilesIsomeric,yes, 528 smilesTitleLine,yes,smilesMolName,yes,smilesMolProps,no 529 530 Default value for compute2DCoords: yes for SMILES input file; no for all other 531 file types. 532 --overwrite 533 Overwrite existing files. 534 -p, --pattern <SMARTS> [default: none] 535 SMARTS pattern for performing search. 536 -u, --useChirality <yes or no> [default: no] 537 Use stereochemistry information for SMARTS search. 538 -w, --workingdir <dir> 539 Location of working directory which defaults to the current directory. 540 541 Examples: 542 To retrieve molecules containing the substructure corresponding to a specified 543 SMARTS pattern and write out a SMILES file, type: 544 545 % RDKitSearchSMARTS.py -p 'c1ccccc1' -i Sample.smi -o SampleOut.smi 546 547 To retrieve molecules containing the substructure corresponding to a specified 548 SMARTS pattern, perform filtering in multiprocessing mode on all available 549 CPUs without loading all data into memory, and write out a SMILES file, type: 550 551 % RDKitSearchSMARTS.py --mp yes -p 'c1ccccc1' -i Sample.smi -o SampleOut.smi 552 553 To retrieve molecules containing the substructure corresponding to a specified 554 SMARTS pattern, perform filtering in multiprocessing mode on all available 555 CPUs by loading all data into memory, and write out a SMILES file, type: 556 557 % RDKitSearchSMARTS.py --mp yes --mpParams "inputDataMode,InMemory" 558 -p 'c1ccccc1' -i Sample.smi -o SampleOut.smi 559 560 To retrieve molecules containing the substructure corresponding to a specified 561 SMARTS pattern, perform filtering in multiprocessing mode on specific number 562 of CPUs and chunk size without loading all data into memory, and write out 563 a SMILES file, type: 564 565 % RDKitSearchSMARTS.py --mp yes --mpParams "inputDataMode,Lazy, 566 numProcesses,4,chunkSize,8" -p 'c1ccccc1' -i Sample.smi -o SampleOut.smi 567 568 To only count the number of molecules containing the substructure corresponding 569 to a specified SMARTS pattern without writing out any file, type: 570 571 % RDKitSearchSMARTS.py -m count -p 'c1ccccc1' -i Sample.smi 572 573 To count the number of molecules in a SD file not containing the substructure 574 corresponding to a specified SMARTS pattern and write out a SD file, type: 575 576 % RDKitSearchSMARTS.py -n yes -p 'c1ccccc1' -i Sample.sdf -o SampleOut.sdf 577 578 To retrieve molecules containing the substructure corresponding to a specified 579 SMARTS pattern from a CSV SMILES file, SMILES strings in column 1, name in 580 and write out a SD file, type: 581 582 % RDKitSearchSMARTS.py -p 'c1ccccc1' --infileParams 583 "smilesDelimiter,comma,smilesTitleLine,yes,smilesColumn,1, 584 smilesNameColumn,2" --outfileParams "compute2DCoords,yes" 585 -i SampleSMILES.csv -o SampleOut.sdf 586 587 Author: 588 Manish Sud(msud@san.rr.com) 589 590 See also: 591 RDKitConvertFileFormat.py, RDKitFilterPAINS.py, RDKitSearchFunctionalGroups.py 592 593 Copyright: 594 Copyright (C) 2026 Manish Sud. All rights reserved. 595 596 The functionality available in this script is implemented using RDKit, an 597 open source toolkit for cheminformatics developed by Greg Landrum. 598 599 This file is part of MayaChemTools. 600 601 MayaChemTools is free software; you can redistribute it and/or modify it under 602 the terms of the GNU Lesser General Public License as published by the Free 603 Software Foundation; either version 3 of the License, or (at your option) any 604 later version. 605 606 """ 607 608 if __name__ == "__main__": 609 main()