1 #!/bin/env python 2 # 3 # File: RDKitRemoveSalts.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.SaltRemover import SaltRemover 42 from rdkit.Chem.SaltRemover import InputFormat 43 from rdkit.Chem import AllChem 44 except ImportError as ErrMsg: 45 sys.stderr.write("\nFailed to import RDKit module/package: %s\n" % ErrMsg) 46 sys.stderr.write("Check/update your RDKit environment and try again.\n\n") 47 sys.exit(1) 48 49 # MayaChemTools imports... 50 sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), "..", "lib", "Python")) 51 try: 52 from docopt import docopt 53 import MiscUtil 54 import RDKitUtil 55 except ImportError as ErrMsg: 56 sys.stderr.write("\nFailed to import MayaChemTools module/package: %s\n" % ErrMsg) 57 sys.stderr.write("Check/update your MayaChemTools environment and try again.\n\n") 58 sys.exit(1) 59 60 ScriptName = os.path.basename(sys.argv[0]) 61 Options = {} 62 OptionsInfo = {} 63 64 65 def main(): 66 """Start execution of the script.""" 67 68 MiscUtil.PrintInfo( 69 "\n%s (RDKit v%s; MayaChemTools v%s; %s): Starting...\n" 70 % (ScriptName, rdBase.rdkitVersion, MiscUtil.GetMayaChemToolsVersion(), time.asctime()) 71 ) 72 73 (WallClockTime, ProcessorTime) = MiscUtil.GetWallClockAndProcessorTime() 74 75 # Retrieve command line arguments and options... 76 RetrieveOptions() 77 78 # Process and validate command line arguments and options... 79 ProcessOptions() 80 81 # Perform actions required by the script... 82 RemoveSalts() 83 84 MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName) 85 MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime)) 86 87 88 def RemoveSalts(): 89 """Identify and remove salts from molecules.""" 90 91 # Setup a molecule reader... 92 MiscUtil.PrintInfo("\nProcessing file %s..." % OptionsInfo["Infile"]) 93 Mols = RDKitUtil.ReadMolecules(OptionsInfo["Infile"], **OptionsInfo["InfileParams"]) 94 95 # Set up a molecule writer... 96 Writer = SetupMoleculeWriter() 97 98 MolCount, ValidMolCount, SaltsMolCount = ProcessMolecules(Mols, Writer) 99 100 if Writer is not None: 101 Writer.close() 102 103 MiscUtil.PrintInfo("\nTotal number of molecules: %d" % MolCount) 104 MiscUtil.PrintInfo("Number of valid molecules: %d" % ValidMolCount) 105 MiscUtil.PrintInfo("Number of ignored molecules: %d" % (MolCount - ValidMolCount)) 106 107 MiscUtil.PrintInfo("\nNumber of molecules containing salts: %d" % (SaltsMolCount)) 108 109 110 def ProcessMolecules(Mols, Writer): 111 """Process and remove salts from molecules.""" 112 113 if OptionsInfo["MPMode"]: 114 return ProcessMoleculesUsingMultipleProcesses(Mols, Writer) 115 else: 116 return ProcessMoleculesUsingSingleProcess(Mols, Writer) 117 118 119 def ProcessMoleculesUsingSingleProcess(Mols, Writer): 120 """Process and remove salts from molecules using a single process.""" 121 122 MiscUtil.PrintInfo("\nRemoving salts...") 123 124 Compute2DCoords = OptionsInfo["OutfileParams"]["Compute2DCoords"] 125 SetSMILESMolProps = OptionsInfo["OutfileParams"]["SetSMILESMolProps"] 126 127 # Set up a salt remover... 128 Remover = SetupSaltRemover() 129 130 (MolCount, ValidMolCount, SaltsMolCount) = [0] * 3 131 FirstMol = True 132 for Mol in Mols: 133 MolCount += 1 134 135 if Mol is None: 136 continue 137 138 if RDKitUtil.IsMolEmpty(Mol): 139 MolName = RDKitUtil.GetMolName(Mol, MolCount) 140 MiscUtil.PrintWarning("Ignoring empty molecule: %s" % MolName) 141 continue 142 143 ValidMolCount += 1 144 if FirstMol: 145 FirstMol = False 146 if SetSMILESMolProps: 147 RDKitUtil.SetWriterMolProps(Writer, Mol) 148 149 UnsaltedMol, SaltyStatus = RemoveMolSalts(Mol, Remover, MolCount) 150 151 if SaltyStatus: 152 SaltsMolCount += 1 153 154 WriteMolecule(Writer, UnsaltedMol, Compute2DCoords) 155 156 return (MolCount, ValidMolCount, SaltsMolCount) 157 158 159 def ProcessMoleculesUsingMultipleProcesses(Mols, Writer): 160 """Process and remove salts from molecules using multiprocessing.""" 161 162 MiscUtil.PrintInfo("\nRemoving salts using multiprocessing...") 163 164 MPParams = OptionsInfo["MPParams"] 165 Compute2DCoords = OptionsInfo["OutfileParams"]["Compute2DCoords"] 166 167 # Setup data for initializing a worker process... 168 InitializeWorkerProcessArgs = ( 169 MiscUtil.ObjectToBase64EncodedString(Options), 170 MiscUtil.ObjectToBase64EncodedString(OptionsInfo), 171 ) 172 173 # Setup a encoded mols data iterable for a worker process by pickling only public 174 # and private molecule properties... 175 WorkerProcessDataIterable = RDKitUtil.GenerateBase64EncodedMolStrings(Mols) 176 177 # Setup process pool along with data initialization for each process... 178 MiscUtil.PrintInfo( 179 "\nConfiguring multiprocessing using %s method..." 180 % ("mp.Pool.imap()" if re.match("^Lazy$", MPParams["InputDataMode"], re.I) else "mp.Pool.map()") 181 ) 182 MiscUtil.PrintInfo( 183 "NumProcesses: %s; InputDataMode: %s; ChunkSize: %s\n" 184 % ( 185 MPParams["NumProcesses"], 186 MPParams["InputDataMode"], 187 ("automatic" if MPParams["ChunkSize"] is None else MPParams["ChunkSize"]), 188 ) 189 ) 190 191 ProcessPool = mp.Pool(MPParams["NumProcesses"], InitializeWorkerProcess, InitializeWorkerProcessArgs) 192 193 # Start processing... 194 if re.match("^Lazy$", MPParams["InputDataMode"], re.I): 195 Results = ProcessPool.imap(WorkerProcess, WorkerProcessDataIterable, MPParams["ChunkSize"]) 196 elif re.match("^InMemory$", MPParams["InputDataMode"], re.I): 197 Results = ProcessPool.map(WorkerProcess, WorkerProcessDataIterable, MPParams["ChunkSize"]) 198 else: 199 MiscUtil.PrintError( 200 'The value, %s, specified for "--inputDataMode" is not supported.' % (MPParams["InputDataMode"]) 201 ) 202 203 SetSMILESMolProps = OptionsInfo["OutfileParams"]["SetSMILESMolProps"] 204 205 (MolCount, ValidMolCount, SaltsMolCount) = [0] * 3 206 FirstMol = True 207 for Result in Results: 208 MolCount += 1 209 MolIndex, EncodedMol, SaltyStatus = Result 210 211 if EncodedMol is None: 212 continue 213 ValidMolCount += 1 214 215 Mol = RDKitUtil.MolFromBase64EncodedMolString(EncodedMol) 216 217 if FirstMol: 218 FirstMol = False 219 if SetSMILESMolProps: 220 RDKitUtil.SetWriterMolProps(Writer, Mol) 221 222 if SaltyStatus: 223 SaltsMolCount += 1 224 225 WriteMolecule(Writer, Mol, Compute2DCoords) 226 227 return (MolCount, ValidMolCount, SaltsMolCount) 228 229 230 def InitializeWorkerProcess(*EncodedArgs): 231 """Initialize data for a worker process.""" 232 233 global Options, OptionsInfo 234 235 MiscUtil.PrintInfo("Starting process (PID: %s)..." % os.getpid()) 236 237 # Decode Options and OptionInfo... 238 Options = MiscUtil.ObjectFromBase64EncodedString(EncodedArgs[0]) 239 OptionsInfo = MiscUtil.ObjectFromBase64EncodedString(EncodedArgs[1]) 240 241 # Set up salt remover... 242 OptionsInfo["SaltRemover"] = SetupSaltRemover() 243 244 245 def WorkerProcess(EncodedMolInfo): 246 """Process data for a worker process.""" 247 248 MolIndex, EncodedMol = EncodedMolInfo 249 250 if EncodedMol is None: 251 return [MolIndex, None, False] 252 253 Mol = RDKitUtil.MolFromBase64EncodedMolString(EncodedMol) 254 if RDKitUtil.IsMolEmpty(Mol): 255 MolName = RDKitUtil.GetMolName(Mol, (MolIndex + 1)) 256 MiscUtil.PrintWarning("Ignoring empty molecule: %s" % MolName) 257 return [MolIndex, None, False] 258 259 Mol, SaltyStatus = RemoveMolSalts(Mol, OptionsInfo["SaltRemover"], (MolIndex + 1)) 260 EncodedMol = RDKitUtil.MolToBase64EncodedMolString( 261 Mol, PropertyPickleFlags=Chem.PropertyPickleOptions.MolProps | Chem.PropertyPickleOptions.PrivateProps 262 ) 263 264 return [MolIndex, EncodedMol, SaltyStatus] 265 266 267 def RemoveMolSalts(Mol, Remover, MolNum): 268 """Remove salts from mol and return unsalted mol along with mol salty status.""" 269 270 UnsaltedMol = Mol 271 SaltyStatus = False 272 273 if Remover is not None: 274 KeptMol, DeletedMols = Remover.StripMolWithDeleted(Mol, dontRemoveEverything=False) 275 if len(DeletedMols) >= 1: 276 SaltyStatus = True 277 if RDKitUtil.IsMolEmpty(KeptMol): 278 if len(DeletedMols) >= 1: 279 # Take the larged fragment from DeletedMols 280 UnsaltedMol = GetLargestMol(DeletedMols) 281 else: 282 # Use largest fragment as unsalted molecule... 283 MolFrags = Chem.GetMolFrags(Mol, asMols=True) 284 if len(MolFrags) > 1: 285 # Keep the largest fragment as unsalted molecule... 286 SaltyStatus = True 287 UnsaltedMol = GetLargestMol(MolFrags) 288 289 if SaltyStatus: 290 Chem.SanitizeMol(UnsaltedMol) 291 MolName = RDKitUtil.GetMolName(Mol, MolNum) 292 if len(MolName): 293 UnsaltedMol.SetProp("_Name", MolName) 294 295 # Set mol properties... 296 for DataLabel in Mol.GetPropNames(includePrivate=False, includeComputed=False): 297 DataProp = Mol.GetProp(DataLabel) 298 UnsaltedMol.SetProp(DataLabel, DataProp) 299 300 return (UnsaltedMol, SaltyStatus) 301 302 303 def GetLargestMol(Mols): 304 """Get largest mol from list of mols.""" 305 306 LargestMol = None 307 LargestMolSize = -1 308 for Mol in Mols: 309 Size = Mol.GetNumAtoms() 310 if Size > LargestMolSize: 311 LargestMol = Mol 312 LargestMolSize = Size 313 314 return LargestMol 315 316 317 def SetupSaltRemover(): 318 """Setup a salt remover.""" 319 320 Remover = None 321 if OptionsInfo["SaltsByComponentsMode"]: 322 return Remover 323 324 return SaltRemover( 325 defnFilename=OptionsInfo["SaltsFile"], defnData=OptionsInfo["SaltsSMARTS"], defnFormat=InputFormat.SMARTS 326 ) 327 328 329 def WriteMolecule(Writer, Mol, Compute2DCoords): 330 """Write out molecule.""" 331 332 if OptionsInfo["CountMode"]: 333 return 334 335 if Compute2DCoords: 336 AllChem.Compute2DCoords(Mol) 337 338 Writer.write(Mol) 339 340 341 def SetupMoleculeWriter(): 342 """Setup a molecule writer.""" 343 344 Writer = None 345 if OptionsInfo["CountMode"]: 346 return Writer 347 348 Writer = RDKitUtil.MoleculesWriter(OptionsInfo["Outfile"], **OptionsInfo["OutfileParams"]) 349 if Writer is None: 350 MiscUtil.PrintError("Failed to setup a writer for output fie %s " % OptionsInfo["Outfile"]) 351 MiscUtil.PrintInfo("Generating file %s..." % OptionsInfo["Outfile"]) 352 353 return Writer 354 355 356 def ProcessOptions(): 357 """Process and validate command line arguments and options.""" 358 359 MiscUtil.PrintInfo("Processing options...") 360 361 # Validate options... 362 ValidateOptions() 363 364 OptionsInfo["Infile"] = Options["--infile"] 365 OptionsInfo["InfileParams"] = MiscUtil.ProcessOptionInfileParameters( 366 "--infileParams", Options["--infileParams"], Options["--infile"] 367 ) 368 369 OptionsInfo["Outfile"] = Options["--outfile"] 370 OptionsInfo["OutfileParams"] = MiscUtil.ProcessOptionOutfileParameters( 371 "--outfileParams", Options["--outfileParams"], Options["--infile"], Options["--outfile"] 372 ) 373 374 OptionsInfo["Overwrite"] = Options["--overwrite"] 375 376 OptionsInfo["CountMode"] = False 377 if re.match("^count$", Options["--mode"], re.I): 378 OptionsInfo["CountMode"] = True 379 380 OptionsInfo["MPMode"] = True if re.match("^yes$", Options["--mp"], re.I) else False 381 OptionsInfo["MPParams"] = MiscUtil.ProcessOptionMultiprocessingParameters("--mpParams", Options["--mpParams"]) 382 383 SaltsByComponentsMode = False 384 SaltsBySMARTSFileMode = False 385 SaltsBySMARTSMode = False 386 if re.match("^ByComponent$", Options["--saltsMode"], re.I): 387 SaltsByComponentsMode = True 388 elif re.match("^BySMARTSFile$", Options["--saltsMode"], re.I): 389 SaltsBySMARTSFileMode = False 390 elif re.match("^BySMARTS$", Options["--saltsMode"], re.I): 391 SaltsBySMARTSMode = True 392 else: 393 MiscUtil.PrintError( 394 'The salts mode specified, %s, using "--saltsMode" option is not valid.' % Options["--saltsMode"] 395 ) 396 OptionsInfo["SaltsByComponentsMode"] = SaltsByComponentsMode 397 OptionsInfo["SaltsBySMARTSFileMode"] = SaltsBySMARTSFileMode 398 OptionsInfo["SaltsBySMARTSMode"] = SaltsBySMARTSMode 399 400 SaltsFile = None 401 if re.match("^BySMARTSFile$", Options["--saltsMode"], re.I): 402 if not re.match("^auto$", Options["--saltsFile"], re.I): 403 SaltsFile = Options["--saltsFile"] 404 OptionsInfo["SaltsFile"] = SaltsFile 405 406 SaltsSMARTS = None 407 if re.match("^BySMARTS$", Options["--saltsMode"], re.I): 408 if not Options["--saltsSMARTS"]: 409 MiscUtil.PrintError( 410 'No salts SMARTS pattern specified using "--saltsSMARTS" option during "BySMARTS" value of "-s, --saltsMode" option' 411 ) 412 SaltsSMARTS = Options["--saltsSMARTS"].strip(" ") 413 if not len(SaltsSMARTS): 414 MiscUtil.PrintError( 415 'Empty SMARTS pattern specified using "--saltsSMARTS" option during "BySMARTS" value of "-s, --saltsMode" option' 416 ) 417 if re.search(" ", SaltsSMARTS): 418 SaltsSMARTS = re.sub("[ ]+", "\n", SaltsSMARTS) 419 420 OptionsInfo["SaltsSMARTS"] = SaltsSMARTS 421 422 423 def RetrieveOptions(): 424 """Retrieve command line arguments and options.""" 425 426 # Get options... 427 global Options 428 Options = docopt(_docoptUsage_) 429 430 # Set current working directory to the specified directory... 431 WorkingDir = Options["--workingdir"] 432 if WorkingDir: 433 os.chdir(WorkingDir) 434 435 # Handle examples option... 436 if "--examples" in Options and Options["--examples"]: 437 MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_)) 438 sys.exit(0) 439 440 441 def ValidateOptions(): 442 """Validate option values.""" 443 444 MiscUtil.ValidateOptionFilePath("-i, --infile", Options["--infile"]) 445 MiscUtil.ValidateOptionFileExt("-i, --infile", Options["--infile"], "sdf sd smi txt csv tsv") 446 447 if Options["--outfile"]: 448 MiscUtil.ValidateOptionFileExt("-o, --outfile", Options["--outfile"], "sdf sd smi") 449 MiscUtil.ValidateOptionsOutputFileOverwrite( 450 "-o, --outfile", Options["--outfile"], "--overwrite", Options["--overwrite"] 451 ) 452 MiscUtil.ValidateOptionsDistinctFileNames( 453 "-i, --infile", Options["--infile"], "-o, --outfile", Options["--outfile"] 454 ) 455 456 MiscUtil.ValidateOptionTextValue("-m, --mode", Options["--mode"], "remove count") 457 if re.match("^remove$", Options["--mode"], re.I): 458 if not Options["--outfile"]: 459 MiscUtil.PrintError( 460 'The outfile must be specified using "-o, --outfile" during "remove" value of "-m, --mode" option' 461 ) 462 463 MiscUtil.ValidateOptionTextValue("--mp", Options["--mp"], "yes no") 464 465 MiscUtil.ValidateOptionTextValue("--saltsMode", Options["--saltsMode"], "ByComponent BySMARTSFile BySMARTS") 466 467 if re.match("^BySMARTSFile$", Options["--saltsMode"], re.I): 468 if not re.match("^auto$", Options["--saltsFile"], re.I): 469 MiscUtil.ValidateOptionFilePath("--saltsFile", Options["--saltsFile"]) 470 471 472 # Setup a usage string for docopt... 473 _docoptUsage_ = """ 474 RDKitRemoveSalts.py - Remove salts 475 476 Usage: 477 RDKitRemoveSalts.py [--infileParams <Name,Value,...>] [--mode <remove or count>] 478 [--mp <yes or no>] [--mpParams <Name,Value,...>] [--outfileParams <Name,Value,...> ] 479 [--overwrite] [--saltsMode <ByComponent, BySMARTSFile, BySMARTS>] 480 [--saltsFile <FileName or auto>] [--saltsSMARTS <SMARTS>] 481 [-w <dir>] [-o <outfile>] -i <infile> 482 RDKitRemoveSalts.py -h | --help | -e | --examples 483 484 Description: 485 Remove salts from molecules or simply count the number of molecules containing 486 salts. Salts are identified and removed based on either SMARTS strings or by selecting 487 the largest disconnected components in molecules as non-salt portion of molecules. 488 489 The supported input file formats are: SD (.sdf, .sd), SMILES (.smi., csv, .tsv, .txt) 490 491 The supported output file formats are: SD (.sdf, .sd), SMILES (.smi) 492 493 Options: 494 -e, --examples 495 Print examples. 496 -h, --help 497 Print this help message. 498 -i, --infile <infile> 499 Input file name. 500 --infileParams <Name,Value,...> [default: auto] 501 A comma delimited list of parameter name and value pairs for reading 502 molecules from files. The supported parameter names for different file 503 formats, along with their default values, are shown below: 504 505 SD: removeHydrogens,yes,sanitize,yes,strictParsing,yes 506 SMILES: smilesColumn,1,smilesNameColumn,2,smilesDelimiter,space, 507 smilesTitleLine,auto,sanitize,yes 508 509 Possible values for smilesDelimiter: space, comma or tab. 510 -m, --mode <remove or count> [default: remove] 511 Specify whether to remove salts from molecules and write out molecules 512 or or simply count the number of molecules containing salts. 513 --mp <yes or no> [default: no] 514 Use multiprocessing. 515 516 By default, input data is retrieved in a lazy manner via mp.Pool.imap() 517 function employing lazy RDKit data iterable. This allows processing of 518 arbitrary large data sets without any additional requirements memory. 519 520 All input data may be optionally loaded into memory by mp.Pool.map() 521 before starting worker processes in a process pool by setting the value 522 of 'inputDataMode' to 'InMemory' in '--mpParams' option. 523 524 A word to the wise: The default 'chunkSize' value of 1 during 'Lazy' input 525 data mode may adversely impact the performance. The '--mpParams' section 526 provides additional information to tune the value of 'chunkSize'. 527 --mpParams <Name,Value,...> [default: auto] 528 A comma delimited list of parameter name and value pairs to configure 529 multiprocessing. 530 531 The supported parameter names along with their default and possible 532 values are shown below: 533 534 chunkSize, auto 535 inputDataMode, Lazy [ Possible values: InMemory or Lazy ] 536 numProcesses, auto [ Default: mp.cpu_count() ] 537 538 These parameters are used by the following functions to configure and 539 control the behavior of multiprocessing: mp.Pool(), mp.Pool.map(), and 540 mp.Pool.imap(). 541 542 The chunkSize determines chunks of input data passed to each worker 543 process in a process pool by mp.Pool.map() and mp.Pool.imap() functions. 544 The default value of chunkSize is dependent on the value of 'inputDataMode'. 545 546 The mp.Pool.map() function, invoked during 'InMemory' input data mode, 547 automatically converts RDKit data iterable into a list, loads all data into 548 memory, and calculates the default chunkSize using the following method 549 as shown in its code: 550 551 chunkSize, extra = divmod(len(dataIterable), len(numProcesses) * 4) 552 if extra: chunkSize += 1 553 554 For example, the default chunkSize will be 7 for a pool of 4 worker processes 555 and 100 data items. 556 557 The mp.Pool.imap() function, invoked during 'Lazy' input data mode, employs 558 'lazy' RDKit data iterable to retrieve data as needed, without loading all the 559 data into memory. Consequently, the size of input data is not known a priori. 560 It's not possible to estimate an optimal value for the chunkSize. The default 561 chunkSize is set to 1. 562 563 The default value for the chunkSize during 'Lazy' data mode may adversely 564 impact the performance due to the overhead associated with exchanging 565 small chunks of data. It is generally a good idea to explicitly set chunkSize to 566 a larger value during 'Lazy' input data mode, based on the size of your input 567 data and number of processes in the process pool. 568 569 The mp.Pool.map() function waits for all worker processes to process all 570 the data and return the results. The mp.Pool.imap() function, however, 571 returns the the results obtained from worker processes as soon as the 572 results become available for specified chunks of data. 573 574 The order of data in the results returned by both mp.Pool.map() and 575 mp.Pool.imap() functions always corresponds to the input data. 576 -o, --outfile <outfile> 577 Output file name. 578 --outfileParams <Name,Value,...> [default: auto] 579 A comma delimited list of parameter name and value pairs for writing 580 molecules to files. The supported parameter names for different file 581 formats, along with their default values, are shown below: 582 583 SD: compute2DCoords,auto,kekulize,yes,forceV3000,no 584 SMILES: smilesKekulize,no,smilesDelimiter,space, smilesIsomeric,yes, 585 smilesTitleLine,yes,smilesMolName,yes,smilesMolProps,no 586 587 Default value for compute2DCoords: yes for SMILES input file; no for all other 588 file types. 589 --overwrite 590 Overwrite existing files. 591 -s, --saltsMode <ByComponent, BySMARTSFile, BySMARTS> [default: ByComponent] 592 Specify whether to identify and remove salts based on SMARTS strings or 593 by selecting the largest disconnected component as non-salt portion of a 594 molecule. Possible values: ByComponent, BySMARTSFile or BySMARTS. 595 --saltsFile <FileName or auto> [default: auto] 596 Specify a file name containing specification for SMARTS corresponding to salts or 597 use default salts file, Salts.txt, available in RDKit data directory. This option is only 598 used during 'BySMARTSFile' value of '-s, --saltsMode' option. 599 600 RDKit data format: Smarts<tab>Name(optional) 601 602 For example: 603 604 [Cl,Br,I] 605 [N](=O)(O)O 606 [CH3]C(=O)O Acetic acid 607 608 --saltsSMARTS <SMARTS text> 609 Space delimited SMARTS specifications to use for salts identification instead 610 their specifications in '--saltsFile'. This option is only used during 'BySMARTS' 611 value of '-s, --saltsMode' option. 612 -w, --workingdir <dir> 613 Location of working directory which defaults to the current directory. 614 615 Examples: 616 To remove salts from molecules in a SMILES file by keeping largest disconnected 617 components as non-salt portion of molecules and write out a SMILES file, type: 618 619 % RDKitRemoveSalts.py -i Sample.smi -o SampleOut.smi 620 621 To remove salts from molecules in a SMILES file by keeping largest disconnected 622 components as non-salt portion of molecules, perform salt removal in multiprocessing 623 mode on all available CPUs without loading all data into memory, and write out a 624 SMILES file, type: 625 626 % RDKitRemoveSalts.py --mp yes -i Sample.smi -o SampleOut.smi 627 628 To remove salts from molecules in a SMILES file by keeping largest disconnected 629 components as non-salt portion of molecules, perform salt removal in multiprocessing 630 mode on all available CPUs by loading all data into memory, and write out a 631 SMILES file, type: 632 633 % RDKitRemoveSalts.py --mp yes --mpParams "inputDataMode,InMemory" 634 -i Sample.smi -o SampleOut.smi 635 636 To remove salts from molecules in a SMILES file by keeping largest disconnected 637 components as non-salt portion of molecules, perform salt removal in multiprocessing 638 mode on specific number of CPUs and chunk size without loading all data into memory, 639 and write out a SMILES file, type: 640 641 % RDKitRemoveSalts.py --mp yes --mpParams "inputDataMode,Lazy, 642 numProcesses,4,chunkSize,8" -i Sample.smi -o SampleOut.smi 643 644 To count number of molecules containing salts from in a SD file, using largest 645 components as non-salt portion of molecules, without generating any output 646 file, type: 647 648 % RDKitRemoveSalts.py -m count -i Sample.sdf 649 650 To remove salts from molecules in a SMILES file using SMARTS strings in default 651 Salts.txt distributed with RDKit to identify salts and write out a SMILES file, type: 652 653 % RDKitRemoveSalts.py -m remove -s BySMARTSFile -i Sample.smi 654 -o SampleOut.smi 655 656 To remove salts from molecules in a SD file using SMARTS strings in a local 657 CustomSalts.txt to identify salts and write out a SMILES file, type: 658 659 % RDKitRemoveSalts.py -m remove -s BySMARTSFile --saltsFile 660 CustomSalts.txt -i Sample.sdf -o SampleOut.smi 661 662 To remove salts from molecules in a SD file using specified SMARTS to identify 663 salts and write out a SD file, type: 664 665 % RDKitRemoveSalts.py -m remove -s BySMARTS --saltsSMARTS 666 '[Cl,Br,I] [N](=O)(O)O [N](=O)(O)O' 667 -i Sample.sdf -o SampleOut.smi 668 669 To remove salts form molecules from a CSV SMILES file, SMILES strings in column 1, 670 name in column 2, and generate output SD file, type: 671 672 % RDKitRemoveSalts.py --infileParams 673 "smilesDelimiter,comma,smilesTitleLine,yes,smilesColumn,1, 674 smilesNameColumn,2" --outfileParams "compute2DCoords,yes" 675 -i SampleSMILES.csv -o SampleOut.sdf 676 677 Author: 678 Manish Sud(msud@san.rr.com) 679 680 See also: 681 RDKitConvertFileFormat.py, RDKitRemoveDuplicateMolecules.py, 682 RDKitRemoveInvalidMolecules.py, RDKitSearchFunctionalGroups.py, 683 RDKitSearchSMARTS.py, RDKitStandardizeMolecules.py 684 685 Copyright: 686 Copyright (C) 2026 Manish Sud. All rights reserved. 687 688 The functionality available in this script is implemented using RDKit, an 689 open source toolkit for cheminformatics developed by Greg Landrum. 690 691 This file is part of MayaChemTools. 692 693 MayaChemTools is free software; you can redistribute it and/or modify it under 694 the terms of the GNU Lesser General Public License as published by the Free 695 Software Foundation; either version 3 of the License, or (at your option) any 696 later version. 697 698 """ 699 700 if __name__ == "__main__": 701 main()