MayaChemTools

   1 #!/bin/env python
   2 #
   3 # File: OpenMMPerformMDSimulation.py
   4 # Author: Manish Sud <msud@san.rr.com>
   5 #
   6 # Copyright (C) 2025 Manish Sud. All rights reserved.
   7 #
   8 # The functionality available in this script is implemented using OpenMM, an
   9 # open source molecuar simulation package.
  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 # OpenMM imports...
  38 try:
  39     import openmm as mm
  40     import openmm.app
  41 except ImportError as ErrMsg:
  42     sys.stderr.write("\nFailed to import OpenMM related module/package: %s\n" % ErrMsg)
  43     sys.stderr.write("Check/update your OpenMM environment and try again.\n\n")
  44     sys.exit(1)
  45 
  46 # MDTraj import...
  47 try:
  48     import mdtraj
  49 except ImportError as ErrMsg:
  50     sys.stderr.write("\nFailed to import MDTraj: %s\n" % ErrMsg)
  51     sys.stderr.write("Check/update your MDTraj environment and try again.\n\n")
  52     sys.exit(1)
  53 
  54 # MayaChemTools imports...
  55 try:
  56     from docopt import docopt
  57     import MiscUtil
  58     import OpenMMUtil
  59 except ImportError as ErrMsg:
  60     sys.stderr.write("\nFailed to import MayaChemTools module/package: %s\n" % ErrMsg)
  61     sys.stderr.write("Check/update your MayaChemTools environment and try again.\n\n")
  62     sys.exit(1)
  63 
  64 ScriptName = os.path.basename(sys.argv[0])
  65 Options = {}
  66 OptionsInfo = {}
  67 
  68 def main():
  69     """Start execution of the script."""
  70     
  71     MiscUtil.PrintInfo("\n%s (OpenMM v%s; MayaChemTools v%s; %s): Starting...\n" % (ScriptName, mm.Platform.getOpenMMVersion(), MiscUtil.GetMayaChemToolsVersion(), time.asctime()))
  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     PerformMinimization()
  83     
  84     MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName)
  85     MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime))
  86 
  87 def PerformMinimization():
  88     """Perform minimization."""
  89 
  90     # Prepare system for simulation...
  91     System, Topology, Positions = PrepareSystem()
  92 
  93     # Freeze and restraint atoms...
  94     FreezeRestraintAtoms(System, Topology, Positions)
  95 
  96     # Setup integrator...
  97     Integrator = SetupIntegrator()
  98 
  99     # Setup simulation...
 100     Simulation = SetupSimulation(System, Integrator, Topology, Positions)
 101 
 102     # Perform energy minimization...
 103     PerformEnergyMinimization(Simulation)
 104 
 105 def PrepareSystem():
 106     """Prepare system for simulation."""
 107 
 108     System, Topology, Positions  = OpenMMUtil.InitializeSystem(OptionsInfo["Infile"], OptionsInfo["ForcefieldParams"], OptionsInfo["SystemParams"], OptionsInfo["WaterBox"], OptionsInfo["WaterBoxParams"], OptionsInfo["SmallMolFile"], OptionsInfo["SmallMolID"])
 109     
 110     # Write out a PDB file for the system...
 111     MiscUtil.PrintInfo("\nWriting PDB file %s..." % OptionsInfo["InitialPDBOutfile"])
 112     OpenMMUtil.WritePDBFile(OptionsInfo["InitialPDBOutfile"], Topology, Positions, OptionsInfo["OutputParams"]["PDBOutKeepIDs"])
 113 
 114     return (System, Topology, Positions)
 115 
 116 def SetupIntegrator():
 117     """Setup integrator. """
 118 
 119     Integrator = OpenMMUtil.InitializeIntegrator(OptionsInfo["IntegratorParams"], OptionsInfo["SystemParams"]["ConstraintErrorTolerance"])
 120 
 121     return Integrator
 122 
 123 def SetupSimulation(System, Integrator, Topology, Positions):
 124     """Setup simulation. """
 125 
 126     Simulation = OpenMMUtil.InitializeSimulation(System, Integrator, Topology, Positions, OptionsInfo["PlatformParams"])
 127 
 128     return Simulation
 129 
 130 def PerformEnergyMinimization(Simulation):
 131     """Perform energy minimization."""
 132 
 133     SimulationParams = OpenMMUtil.SetupSimulationParameters(OptionsInfo["SimulationParams"])
 134 
 135     OutputParams = OptionsInfo["OutputParams"]
 136 
 137     # Setup a local minimization reporter...
 138     MinimizeReporter = None
 139     if OutputParams["MinimizationDataStdout"] or OutputParams["MinimizationDataLog"]:
 140         MinimizeReporter = LocalMinimizationReporter()
 141 
 142     if MinimizeReporter is not None:
 143         MiscUtil.PrintInfo("\nAdding minimization reporters...")
 144         if OutputParams["MinimizationDataLog"]:
 145             MiscUtil.PrintInfo("Adding data log minimization reporter (Steps: %s; File: %s)..." % (OutputParams["MinimizationDataSteps"], OutputParams["MinimizationDataLogFile"]))
 146         if OutputParams["MinimizationDataStdout"]:
 147             MiscUtil.PrintInfo("Adding data stdout minimization reporter (Steps: %s)..." % (OutputParams["MinimizationDataSteps"]))
 148     else:
 149         MiscUtil.PrintInfo("\nSkipping addition of minimization reporters...")
 150         
 151     MaxSteps = SimulationParams["MinimizationMaxSteps"]
 152 
 153     MaxStepsMsg = "MaxSteps: %s" % ("UntilConverged" if MaxSteps == 0 else MaxSteps)
 154     ToleranceMsg = "Tolerance: %.2f kcal/mol/A (%.2f kjoules/mol/nm)" % (SimulationParams["MinimizationToleranceInKcal"], SimulationParams["MinimizationToleranceInJoules"])
 155 
 156     MiscUtil.PrintInfo("\nPerforming energy minimization (%s; %s)..." % (MaxStepsMsg, ToleranceMsg))
 157 
 158     if OutputParams["MinimizationDataStdout"]:
 159         HeaderLine = SetupMinimizationDataOutHeaderLine()
 160         print("\n%s" % HeaderLine)
 161 
 162     Simulation.minimizeEnergy(tolerance = SimulationParams["MinimizationTolerance"], maxIterations = MaxSteps, reporter = MinimizeReporter)
 163 
 164     if OutputParams["MinimizationDataLog"]:
 165         WriteMinimizationDataLogFile(MinimizeReporter.DataOutValues)
 166 
 167     # Write out minimized structure...
 168     MiscUtil.PrintInfo("\nWriting PDB file %s..." % OptionsInfo["MinimizedPDBOutfile"])
 169     OpenMMUtil.WriteSimulationStatePDBFile(Simulation, OptionsInfo["MinimizedPDBOutfile"], OptionsInfo["OutputParams"]["PDBOutKeepIDs"])
 170 
 171 class LocalMinimizationReporter(mm.MinimizationReporter):
 172     """Setup a local minimization reporter. """
 173 
 174     (DataSteps, DataOutTypeList, DataOutDelimiter, StdoutStatus) = [None] * 4
 175 
 176     DataOutValues = []
 177     First = True
 178 
 179     def report(self, Iteration, PositonsList, GradientsList, DataStatisticsMap):
 180         """Report and track minimization."""
 181 
 182         if self.First:
 183             # Initialize...
 184             self.DataSteps = OptionsInfo["OutputParams"]["MinimizationDataSteps"]
 185             self.DataOutTypeList = OptionsInfo["OutputParams"]["MinimizationDataOutTypeOpenMMNameList"]
 186             self.DataOutDelimiter = OptionsInfo["OutputParams"]["DataOutDelimiter"]
 187             self.StdoutStatus = True if OptionsInfo["OutputParams"]["MinimizationDataStdout"] else False
 188 
 189             self.First = False
 190 
 191         if Iteration % self.DataSteps == 0:
 192             # Setup data values...
 193             DataValues = []
 194             DataValues.append("%s" % Iteration)
 195             for DataType in self.DataOutTypeList:
 196                 DataValue = "%.4f" % DataStatisticsMap[DataType]
 197                 DataValues.append(DataValue)
 198 
 199             # Track data...
 200             self.DataOutValues.append(DataValues)
 201 
 202             # Print data values...
 203             if self.StdoutStatus:
 204                 print("%s" % self.DataOutDelimiter.join(DataValues))
 205                 
 206         # This method must return a bool. You may return true for early termination.
 207         return False
 208 
 209 def WriteMinimizationDataLogFile(DataOutValues):
 210     """Write minimization data log file. """
 211 
 212     OutputParams = OptionsInfo["OutputParams"]
 213 
 214     Outfile = OutputParams["MinimizationDataLogFile"]
 215     OutDelimiter = OutputParams["DataOutDelimiter"]
 216 
 217     MiscUtil.PrintInfo("\nWriting minimization log file %s..." % Outfile)
 218     OutFH = open(Outfile, "w")
 219 
 220     HeaderLine = SetupMinimizationDataOutHeaderLine()
 221     OutFH.write("%s\n" % HeaderLine)
 222 
 223     for LineWords in DataOutValues:
 224         Line = OutDelimiter.join(LineWords)
 225         OutFH.write("%s\n" % Line)
 226 
 227     OutFH.close()
 228     
 229 def SetupMinimizationDataOutHeaderLine():
 230     """Setup minimization data output header line. """
 231 
 232     LineWords = ["Iteration"]
 233     for Label in OptionsInfo["OutputParams"]["MinimizationDataOutTypeList"]:
 234         if re.match("^(SystemEnergy|RestraintEnergy)$", Label, re.I):
 235             LineWords.append("%s(kjoules/mol)" % Label)
 236         elif re.match("^RestraintStrength$", Label, re.I):
 237             LineWords.append("%s(kjoules/mol/nm^2)" % Label)
 238         else:
 239             LineWords.append(Label)
 240 
 241     Line = OptionsInfo["OutputParams"]["DataOutDelimiter"].join(LineWords)
 242 
 243     return Line
 244 
 245 def FreezeRestraintAtoms(System, Topology, Positions):
 246     """Handle freezing and restraining of atoms."""
 247 
 248     FreezeAtomList, RestraintAtomList = OpenMMUtil.ValidateAndFreezeRestraintAtoms(OptionsInfo["FreezeAtoms"], OptionsInfo["FreezeAtomsParams"], OptionsInfo["RestraintAtoms"], OptionsInfo["RestraintAtomsParams"], OptionsInfo["RestraintSpringConstant"], OptionsInfo["SystemParams"], System, Topology, Positions)
 249 
 250 def ProcessOutfilePrefixParameter():
 251     """Process outfile prefix paramater."""
 252 
 253     OutfilePrefix = Options["--outfilePrefix"]
 254 
 255     if not re.match("^auto$", OutfilePrefix, re.I):
 256         OptionsInfo["OutfilePrefix"] = OutfilePrefix
 257         return
 258 
 259     if OptionsInfo["SmallMolFileMode"]:
 260         OutfilePrefix = "%s_%s_Complex" % (OptionsInfo["InfileRoot"], OptionsInfo["SmallMolFileRoot"])
 261     else:
 262         OutfilePrefix = "%s" % (OptionsInfo["InfileRoot"])
 263 
 264     if re.match("^yes$", Options["--waterBox"], re.I):
 265         OutfilePrefix = "%s_Solvated" % (OutfilePrefix)
 266 
 267     OptionsInfo["OutfilePrefix"] = OutfilePrefix
 268 
 269 def ProcessOutfileNames():
 270     """Process outfile names."""
 271 
 272     OutputParams = OptionsInfo["OutputParams"]
 273 
 274     OutfileParamName = "MinimizationDataLogFile"
 275     OutfileParamValue = OutputParams[OutfileParamName]
 276     if not Options["--overwrite"]:
 277         if os.path.exists(OutfileParamValue):
 278             MiscUtil.PrintError("The file specified, %s, for parameter name, %s, using option \"--outfileParams\" already exist. Use option \"--ov\" or \"--overwrite\" and try again. " % (OutfileParamValue, OutfileParamName))
 279 
 280     InitialPDBOutfile = "%s_Initial.%s" % (OptionsInfo["OutfilePrefix"], OutputParams["PDBOutfileExt"])
 281     MinimizedPDBOutfile = "%s_Minimized.%s" % (OptionsInfo["OutfilePrefix"], OutputParams["PDBOutfileExt"])
 282     for Outfile in [InitialPDBOutfile, MinimizedPDBOutfile]:
 283         if not Options["--overwrite"]:
 284             if os.path.exists(Outfile):
 285                 MiscUtil.PrintError("The file name, %s, generated using option \"--outfilePrefix\" already exist. Use option \"--ov\" or \"--overwrite\" and try again. " % (Outfile))
 286     OptionsInfo["InitialPDBOutfile"] = InitialPDBOutfile
 287     OptionsInfo["MinimizedPDBOutfile"] = MinimizedPDBOutfile
 288 
 289 def ProcessWaterBoxParameters():
 290     """Process water box parameters. """
 291 
 292     OptionsInfo["WaterBox"] = True if re.match("^yes$", Options["--waterBox"], re.I) else False
 293     OptionsInfo["WaterBoxParams"] = OpenMMUtil.ProcessOptionOpenMMWaterBoxParameters("--waterBoxParams", Options["--waterBoxParams"])
 294     
 295     if OptionsInfo["WaterBox"]:
 296         if OptionsInfo["ForcefieldParams"]["ImplicitWater"]:
 297             MiscUtil.PrintInfo("")
 298             MiscUtil.PrintWarning("The value, %s, specified using option \"--waterBox\" may not be valid for the combination of biopolymer and water forcefields, %s and %s, specified using \"--forcefieldParams\". You may consider using a valid combination of biopolymer and water forcefields for explicit water during the addition of a water box." % (Options["--waterBox"], OptionsInfo["ForcefieldParams"]["Biopolymer"], OptionsInfo["ForcefieldParams"]["Water"]))
 299 
 300 def ProcessOptions():
 301     """Process and validate command line arguments and options."""
 302 
 303     MiscUtil.PrintInfo("Processing options...")
 304 
 305     ValidateOptions()
 306 
 307     OptionsInfo["Infile"] = Options["--infile"]
 308     FileDir, FileName, FileExt = MiscUtil.ParseFileName(OptionsInfo["Infile"])
 309     OptionsInfo["InfileRoot"] = FileName
 310 
 311     SmallMolFile = Options["--smallMolFile"]
 312     SmallMolID = Options["--smallMolID"]
 313     SmallMolFileMode = False
 314     SmallMolFileRoot = None
 315     if SmallMolFile is not None:
 316         FileDir, FileName, FileExt = MiscUtil.ParseFileName(SmallMolFile)
 317         SmallMolFileRoot = FileName
 318         SmallMolFileMode = True
 319 
 320     OptionsInfo["SmallMolFile"] = SmallMolFile
 321     OptionsInfo["SmallMolFileRoot"] = SmallMolFileRoot
 322     OptionsInfo["SmallMolFileMode"] = SmallMolFileMode
 323     OptionsInfo["SmallMolID"] = SmallMolID.upper()
 324 
 325     ProcessOutfilePrefixParameter()
 326 
 327     ParamsDefaultInfoOverride = {"MinimizationDataSteps": 100, "MinimizationDataStdout": False, "MinimizationDataLog": True}
 328     OptionsInfo["OutputParams"] = OpenMMUtil.ProcessOptionOpenMMOutputParameters("--outputParams", Options["--outputParams"], OptionsInfo["OutfilePrefix"], ParamsDefaultInfoOverride)
 329     ProcessOutfileNames()
 330 
 331     OptionsInfo["ForcefieldParams"] = OpenMMUtil.ProcessOptionOpenMMForcefieldParameters("--forcefieldParams", Options["--forcefieldParams"])
 332 
 333     OptionsInfo["FreezeAtoms"] = True if re.match("^yes$", Options["--freezeAtoms"], re.I) else False
 334     if OptionsInfo["FreezeAtoms"]:
 335         OptionsInfo["FreezeAtomsParams"] = OpenMMUtil.ProcessOptionOpenMMAtomsSelectionParameters("--freezeAtomsParams", Options["--freezeAtomsParams"])
 336     else:
 337         OptionsInfo["FreezeAtomsParams"] = None
 338 
 339     ParamsDefaultInfoOverride = {"Name": Options["--platform"], "Threads": 1}
 340     OptionsInfo["PlatformParams"] = OpenMMUtil.ProcessOptionOpenMMPlatformParameters("--platformParams", Options["--platformParams"], ParamsDefaultInfoOverride)
 341 
 342     OptionsInfo["RestraintAtoms"] = True if re.match("^yes$", Options["--restraintAtoms"], re.I) else False
 343     if OptionsInfo["RestraintAtoms"]:
 344         OptionsInfo["RestraintAtomsParams"] = OpenMMUtil.ProcessOptionOpenMMAtomsSelectionParameters("--restraintAtomsParams", Options["--restraintAtomsParams"])
 345     else:
 346         OptionsInfo["RestraintAtomsParams"] = None
 347     OptionsInfo["RestraintSpringConstant"] = float(Options["--restraintSpringConstant"])
 348 
 349     OptionsInfo["SystemParams"] = OpenMMUtil.ProcessOptionOpenMMSystemParameters("--systemParams", Options["--systemParams"])
 350 
 351     OptionsInfo["IntegratorParams"] = OpenMMUtil.ProcessOptionOpenMMIntegratorParameters("--integratorParams", Options["--integratorParams"], HydrogenMassRepartioningStatus = OptionsInfo["SystemParams"]["HydrogenMassRepartioning"])
 352 
 353     OptionsInfo["SimulationParams"] = OpenMMUtil.ProcessOptionOpenMMSimulationParameters("--simulationParams", Options["--simulationParams"])
 354 
 355     ProcessWaterBoxParameters()
 356 
 357     OptionsInfo["Overwrite"] = Options["--overwrite"]
 358     
 359 def RetrieveOptions(): 
 360     """Retrieve command line arguments and options."""
 361 
 362     # Get options...
 363     global Options
 364     Options = docopt(_docoptUsage_)
 365 
 366     # Set current working directory to the specified directory...
 367     WorkingDir = Options["--workingdir"]
 368     if WorkingDir:
 369         os.chdir(WorkingDir)
 370     
 371     # Handle examples option...
 372     if "--examples" in Options and Options["--examples"]:
 373         MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_))
 374         sys.exit(0)
 375 
 376 def ValidateOptions():
 377     """Validate option values."""
 378 
 379     MiscUtil.ValidateOptionFilePath("-i, --infile", Options["--infile"])
 380     MiscUtil.ValidateOptionFileExt("-i, --infile", Options["--infile"], "pdb cif")
 381 
 382     FileDir, FileName, FileExt = MiscUtil.ParseFileName(Options["--infile"])
 383     OutfilePrefix = Options["--outfilePrefix"]
 384     if not re.match("^auto$", OutfilePrefix, re.I):
 385         if re.match("^(%s)$" % OutfilePrefix, FileName, re.I):
 386             MiscUtil.PrintError("The value specified, %s, for option \"--outfilePrefix\" is not valid. You must specify a value different from, %s, the root of infile name." % (OutfilePrefix, FileName))
 387 
 388     if Options["--smallMolFile"] is not None:
 389         MiscUtil.ValidateOptionFilePath("-l, --smallMolFile", Options["--smallMolFile"])
 390         MiscUtil.ValidateOptionFileExt("-l, --smallMolFile", Options["--smallMolFile"], "sd sdf")
 391 
 392     SmallMolID = Options["--smallMolID"]
 393     if len(SmallMolID) != 3:
 394         MiscUtil.PrintError("The value specified, %s, for option \"--smallMolID\" is not valid. You must specify a three letter small molecule ID." % (SmallMolID))
 395 
 396     MiscUtil.ValidateOptionTextValue("--freezeAtoms", Options["--freezeAtoms"], "yes no")
 397     if re.match("^yes$", Options["--freezeAtoms"], re.I):
 398         if Options["--freezeAtomsParams"] is None:
 399             MiscUtil.PrintError("No value specified for option \"--freezeAtomsParams\". You must specify valid values during, yes, value for \"--freezeAtoms\" option.")
 400     
 401     MiscUtil.ValidateOptionTextValue("-p, --platform", Options["--platform"], "CPU CUDA OpenCL Reference")
 402 
 403     MiscUtil.ValidateOptionTextValue("--restraintAtoms", Options["--restraintAtoms"], "yes no")
 404     if re.match("^yes$", Options["--restraintAtoms"], re.I):
 405         if Options["--restraintAtomsParams"] is None:
 406             MiscUtil.PrintError("No value specified for option \"--restraintAtomsParams\". You must specify valid values during, yes, value for \"--restraintAtoms\" option.")
 407     
 408     MiscUtil.ValidateOptionFloatValue("--restraintSpringConstant", Options["--restraintSpringConstant"], {">": 0})
 409 
 410     MiscUtil.ValidateOptionTextValue("--waterBox", Options["--waterBox"], "yes no")
 411 
 412 # Setup a usage string for docopt...
 413 _docoptUsage_ = """
 414 OpenMMPerformMinimization.py - Perform an energy minimization
 415 
 416 Usage:
 417     OpenMMPerformMDSimulation.py [--forcefieldParams <Name,Value,..>] [--freezeAtoms <yes or no>]
 418                                  [--freezeAtomsParams <Name,Value,..>] [--integratorParams <Name,Value,..>]
 419                                  [--outputParams <Name,Value,..>] [--outfilePrefix <text>]
 420                                  [--overwrite] [--platform <text>] [--platformParams <Name,Value,..>]
 421                                  [--restraintAtoms <yes or no>]
 422                                  [--restraintAtomsParams <Name,Value,..>] [--restraintSpringConstant <number>]
 423                                  [--simulationParams <Name,Value,..>] [--smallMolFile <SmallMolFile>] [--smallMolID <text>]
 424                                  [--systemParams <Name,Value,..>] [--waterBox <yes or no>]
 425                                  [--waterBoxParams <Name,Value,..>] [-w <dir>] -i <infile>
 426     OpenMMPerformMDSimulation.py -h | --help | -e | --examples
 427 
 428 Description:
 429     Perform energy minimization for a macromolecule or a macromolecule in a
 430     complex with small molecule. You may optionally add a water box and
 431     freeze/restraint atoms to your system before minimization.
 432 
 433     The input file must contain a macromolecule already prepared for simulation.
 434     The preparation of the macromolecule for a simulation generally involves the
 435     following: tasks:  Identification and replacement non-standard residues;
 436     Addition of missing residues; Addition of missing heavy atoms; Addition of
 437     missing hydrogens; Addition of a water box which is optional.
 438 
 439     In addition, the small molecule input file must contain a molecule already
 440     prepared for simulation. It must contain  appropriate 3D coordinates relative
 441     to the macromolecule along with no missing hydrogens.
 442 
 443     The supported macromolecule input file formats are:  PDB (.pdb) and
 444     CIF (.cif)
 445 
 446     The supported small molecule input file format are : SD (.sdf, .sd)
 447 
 448     Possible outfile prefixes:
 449          
 450         <InfieRoot>
 451         <InfieRoot>_Solvated
 452         <InfieRoot>_<SmallMolFileRoot>_Complex
 453         <InfieRoot>_<SmallMolFileRoot>_Complex_Solvated
 454          
 455     Possible output files:
 456          
 457         <OutfilePrefix>_Initial.<pdb or cif>
 458         <OutfilePrefix>_Minimized.<pdb or cif>
 459         <OutfilePrefix>_Minimization.csv
 460          
 461 Options:
 462     -e, --examples
 463         Print examples.
 464     -f, --forcefieldParams <Name,Value,..>  [default: auto]
 465         A comma delimited list of parameter name and value pairs for biopolymer,
 466         water, and small molecule forcefields.
 467         
 468         The supported parameter names along with their default values are
 469         are shown below:
 470             
 471             biopolymer, amber14-all.xml  [ Possible values: Any Valid value ]
 472             smallMolecule, openff-2.2.1  [ Possible values: Any Valid value ]
 473             water, auto  [ Possible values: Any Valid value ]
 474             additional, none [ Possible values: Space delimited list of any
 475                 valid value ]
 476             
 477         Possible biopolymer forcefield values:
 478             
 479             amber14-all.xml, amber99sb.xml, amber99sbildn.xml, amber03.xml,
 480             amber10.xml
 481             charmm36.xml, charmm_polar_2019.xml
 482             amoeba2018.xml
 483         
 484         Possible small molecule forcefield values:
 485             
 486             openff-2.2.1, openff-2.0.0, openff-1.3.1, openff-1.2.1,
 487             openff-1.1.1, openff-1.1.0,...
 488             smirnoff99Frosst-1.1.0, smirnoff99Frosst-1.0.9,...
 489             gaff-2.11, gaff-2.1, gaff-1.81, gaff-1.8, gaff-1.4,...
 490         
 491         The default water forcefield valus is dependent on the type of the
 492         biopolymer forcefield as shown below:
 493             
 494             Amber: amber14/tip3pfb.xml
 495             CHARMM: charmm36/water.xml or None for charmm_polar_2019.xml
 496             Amoeba: None (Explicit)
 497             
 498         Possible water forcefield values:
 499             
 500             amber14/tip3p.xml, amber14/tip3pfb.xml, amber14/spce.xml,
 501             amber14/tip4pew.xml, amber14/tip4pfb.xml,
 502             charmm36/water.xml, charmm36/tip3p-pme-b.xml,
 503             charmm36/tip3p-pme-f.xml, charmm36/spce.xml,
 504             charmm36/tip4pew.xml, charmm36/tip4p2005.xml,
 505             charmm36/tip5p.xml, charmm36/tip5pew.xml,
 506             implicit/obc2.xml, implicit/GBn.xml, implicit/GBn2.xml,
 507             amoeba2018_gk.xml (Implict water)
 508             None (Explicit water for amoeba)
 509         
 510         The additional forcefield value is a space delimited list of any valid
 511         forcefield values and is passed on to the OpenMMForcefields
 512         SystemGenerator along with the specified forcefield  values for
 513         biopolymer, water, and mall molecule. Possible additional forcefield
 514         values are:
 515             
 516             amber14/DNA.OL15.xml amber14/RNA.OL3.xml
 517             amber14/lipid17.xml amber14/GLYCAM_06j-1.xml
 518             ... ... ...
 519             
 520         You may specify any valid forcefield names supported by OpenMM. No
 521         explicit validation is performed.
 522     --freezeAtoms <yes or no>  [default: no]
 523         Freeze atoms during energy minimization. The specified atoms are kept
 524         completely fixed by setting their masses to zero. Their positions do not
 525         change during  energy minimization.
 526     --freezeAtomsParams <Name,Value,..>
 527         A comma delimited list of parameter name and value pairs for freezing
 528         atoms during energy minimization. You must specify these parameters for 'yes'
 529         value of '--freezeAtoms' option.
 530         
 531         The supported parameter names along with their default values are
 532         are shown below:
 533             
 534             selection, none [ Possible values: CAlphaProtein, Ions, Ligand,
 535                 Protein, Residues, or Water ]
 536             selectionSpec, auto [ Possible values: A space delimited list of
 537                 residue names ]
 538             negate, no [ Possible values: yes or no ]
 539             
 540         A brief description of parameters is provided below:
 541             
 542             selection: Atom selection to freeze.
 543             selectionSpec: A space delimited list of residue names for
 544                 selecting atoms to freeze. You must specify its value during
 545                 'Ligand' and 'Protein' value for 'selection'. The default values
 546                 are automatically set for 'CAlphaProtein', 'Ions', 'Protein',
 547                 and 'Water' values of 'selection' as shown below:
 548                 
 549                 CAlphaProtein: List of stadard protein residues from pdbfixer
 550                     for selecting CAlpha atoms.
 551                 Ions: Li Na K Rb Cs Cl Br F I
 552                 Water: HOH
 553                 Protein: List of standard protein residues from pdbfixer.
 554                 
 555             negate: Negate atom selection match to select atoms for freezing.
 556             
 557         In addition, you may specify an explicit space delimited list of residue
 558         names using 'selectionSpec' for any 'selection". The specified residue
 559         names are appended to the appropriate default values during the
 560         selection of atoms for freezing.
 561     -h, --help
 562         Print this help message.
 563     -i, --infile <infile>
 564         Input file name containing a macromolecule.
 565     --integratorParams <Name,Value,..>  [default: auto]
 566         A comma delimited list of parameter name and value pairs for integrator
 567         to setup the system for local energy minimization. No MD simulation is
 568         performed.
 569         
 570         The supported parameter names along with their default values are
 571         are shown below:
 572             
 573             temperature, 300.0 [ Units: kelvin ]
 574             
 575     --outfilePrefix <text>  [default: auto]
 576         File prefix for generating the names of output files. The default value
 577         depends on the names of input files for macromolecule and small molecule
 578         along with the type of statistical ensemble and the nature of the solvation.
 579         
 580         The possible values for outfile prefix are shown below:
 581             
 582             <InfieRoot>
 583             <InfieRoot>_Solvated
 584             <InfieRoot>_<SmallMolFileRoot>_Complex
 585             <InfieRoot>_<SmallMolFileRoot>_Complex_Solvated
 586             
 587     --outputParams <Name,Value,..>  [default: auto]
 588         A comma delimited list of parameter name and value pairs for generating
 589        output during energy minimization..
 590         
 591         The supported parameter names along with their default values are
 592         are shown below:
 593             
 594             minimizationDataSteps, 100
 595             minimizationDataStdout, no  [ Possible values: yes or no ]
 596             minimizationDataLog, yes  [ Possible values: yes or no ]
 597             minimizationDataLogFile, auto  [ Default:
 598                 <OutfilePrefix>_MinimizationOut.csv ]
 599             minimizationDataOutType, auto [ Possible values: A space delimited
 600                list of valid parameter names.  Default: SystemEnergy
 601                RestraintEnergy MaxConstraintError.
 602                Other valid names: RestraintStrength ]
 603             
 604             pdbOutFormat, PDB  [ Possible values: PDB or CIF ]
 605             pdbOutKeepIDs, yes  [ Possible values: yes or no ]
 606             
 607         A brief description of parameters is provided below:
 608             
 609             minimizationDataSteps: Frequency of writing data to stdout
 610                 and log file.
 611             minimizationDataStdout: Write data to stdout.
 612             minimizationDataLog: Write data to log file.
 613             minimizationDataLogFile: Data log fie name.
 614             minimizationDataOutType: Type of data to write to stdout
 615                 and log file.
 616             
 617             pdbOutFormat: Format of output PDB files.
 618             pdbOutKeepIDs: Keep existing chain and residue IDs.
 619             
 620     --overwrite
 621         Overwrite existing files.
 622     -p, --platform <text>  [default: CPU]
 623         Platform to use for running MD simulation. Possible values: CPU, CUDA,
 624        OpenCL, or Reference.
 625     --platformParams <Name,Value,..>  [default: auto]
 626         A comma delimited list of parameter name and value pairs to configure
 627         platform for running energy minimization calculations..
 628         
 629         The supported parameter names along with their default values for
 630         different platforms are shown below:
 631             
 632             CPU:
 633             
 634             threads, 1  [ Possible value: >= 0 or auto.  The value of 'auto'
 635                 or zero implies the use of all available CPUs for threading. ]
 636             
 637             CUDA:
 638             
 639             deviceIndex, auto  [ Possible values: 0, '0 1' etc. ]
 640             deterministicForces, auto [ Possible values: yes or no ]
 641             precision, single  [ Possible values: single, double, or mix ]
 642             tempDirectory, auto [ Possible value: DirName ]
 643             useBlockingSync, auto [ Possible values: yes or no ]
 644             useCpuPme, auto [ Possible values: yes or no ]
 645             
 646             OpenCL:
 647             
 648             deviceIndex, auto  [ Possible values: 0, '0 1' etc. ]
 649             openCLPlatformIndex, auto  [ Possible value: Number]
 650             precision, single  [ Possible values: single, double, or mix ]
 651             useCpuPme, auto [ Possible values: yes or no ]
 652             
 653         A brief description of parameters is provided below:
 654             
 655             CPU:
 656             
 657             threads: Number of threads to use for simulation.
 658             
 659             CUDA:
 660             
 661             deviceIndex: Space delimited list of device indices to use for
 662                 calculations.
 663             deterministicForces: Generate reproducible results at the cost of a
 664                 small decrease in performance.
 665             precision: Number precision to use for calculations.
 666             tempDirectory: Directory name for storing temporary files.
 667             useBlockingSync: Control run-time synchronization between CPU and
 668                 GPU.
 669             useCpuPme: Use CPU-based PME implementation.
 670             
 671             OpenCL:
 672             
 673             deviceIndex: Space delimited list of device indices to use for
 674                 simulation.
 675             openCLPlatformIndex: Platform index to use for calculations.
 676             precision: Number precision to use for calculations.
 677             useCpuPme: Use CPU-based PME implementation.
 678             
 679     --restraintAtoms <yes or no>  [default: no]
 680         Restraint atoms during energy minimization. The motion of specified atoms is
 681         restricted by adding a harmonic force that binds them to their starting
 682         positions. The atoms are not completely fixed unlike freezing of atoms.
 683         Their motion, however, is restricted and they are not able to move far away
 684         from their starting positions during energy minimization.
 685     --restraintAtomsParams <Name,Value,..>
 686         A comma delimited list of parameter name and value pairs for restraining
 687         atoms during energy minimization. You must specify these parameters for 'yes'
 688         value of '--restraintAtoms' option.
 689         
 690         The supported parameter names along with their default values are
 691         are shown below:
 692             
 693             selection, none [ Possible values: CAlphaProtein, Ions, Ligand,
 694                 Protein, Residues, or Water ]
 695             selectionSpec, auto [ Possible values: A space delimited list of
 696                 residue names ]
 697             negate, no [ Possible values: yes or no ]
 698             
 699         A brief description of parameters is provided below:
 700             
 701             selection: Atom selection to restraint.
 702             selectionSpec: A space delimited list of residue names for
 703                 selecting atoms to restraint. You must specify its value during
 704                 'Ligand' and 'Protein' value for 'selection'. The default values
 705                 are automatically set for 'CAlphaProtein', 'Ions', 'Protein',
 706                 and 'Water' values of 'selection' as shown below:
 707                 
 708                 CAlphaProtein: List of stadard protein residues from pdbfixer
 709                   for selecting CAlpha atoms.
 710                 Ions: Li Na K Rb Cs Cl Br F I
 711                 Water: HOH
 712                 Protein: List of standard protein residues from pdbfixer.
 713                 
 714             negate: Negate atom selection match to select atoms for freezing.
 715             
 716         In addition, you may specify an explicit space delimited list of residue
 717         names using 'selectionSpec' for any 'selection". The specified residue
 718         names are appended to the appropriate default values during the
 719         selection of atoms for restraining.
 720     --restraintSpringConstant <number>  [default: 2.5]
 721         Restraint spring constant for applying external restraint force to restraint
 722         atoms relative to their initial positions during 'yes' value of '--restraintAtoms'
 723         option. Default units: kcal/mol/A**2. The default value, 2.5, corresponds to
 724         1046.0 kjoules/mol/nm**2. The default value is automatically converted into
 725         units of kjoules/mol/nm**2 before its usage.
 726     --simulationParams <Name,Value,..>  [default: auto]
 727         A comma delimited list of parameter name and value pairs for simulation.
 728         
 729         The supported parameter names along with their default values are
 730         are shown below:
 731             
 732             minimizationMaxSteps, auto  [ Possible values: >= 0. The value of
 733                 zero implies until the minimization is converged. ]
 734             minimizationTolerance, 0.24  [ Units: kcal/mol/A. The default value
 735                 0.25, corresponds to OpenMM default of value of 10.04
 736                 kjoules/mol/nm. It is automatically converted into OpenMM
 737                 default units before its usage. ]
 738             
 739         A brief description of parameters is provided below:
 740             
 741             minimizationMaxSteps: Maximum number of minimization steps. The
 742                 value of zero implies until the minimization is converged.
 743             minimizationTolerance: Energy convergence tolerance during
 744                 minimization.
 745             
 746     -s, --smallMolFile <SmallMolFile>
 747         Small molecule input file name. The macromolecue and small molecule are
 748         merged for simulation and the complex is written out to a PDB file.
 749     --smallMolID <text>  [default: LIG]
 750         Three letter small molecule residue ID. The small molecule ID corresponds
 751         to the residue name of the small molecule and is written out to a PDB file
 752         containing the complex.
 753     --systemParams <Name,Value,..>  [default: auto]
 754         A comma delimited list of parameter name and value pairs to configure
 755         a system for energy minimization. No MD simulation is performed.
 756         
 757         The supported parameter names along with their default values are
 758         are shown below:
 759             
 760             constraints, BondsInvolvingHydrogens [ Possible values: None,
 761                 WaterOnly, BondsInvolvingHydrogens, AllBonds, or
 762                 AnglesInvolvingHydrogens ]
 763             constraintErrorTolerance, 0.000001
 764             ewaldErrorTolerance, 0.0005
 765             
 766             nonbondedMethodPeriodic, PME [ Possible values: NoCutoff,
 767                 CutoffNonPeriodic, or PME ]
 768             nonbondedMethodNonPeriodic, NoCutoff [ Possible values:
 769                 NoCutoff or CutoffNonPeriodic]
 770             nonbondedCutoff, 1.0 [ Units: nm ]
 771             
 772             hydrogenMassRepartioning, yes [ Possible values: yes or no ]
 773             hydrogenMass, 1.5 [ Units: amu]
 774             
 775             removeCMMotion, yes [ Possible values: yes or no ]
 776             rigidWater, auto [ Possible values: yes or no. Default: 'No' for
 777                'None' value of constraints; Otherwise, yes ]
 778             
 779         A brief description of parameters is provided below:
 780             
 781             constraints: Type of system constraints to use for simulation. These
 782                 constraints are different from freezing and restraining of any
 783                 atoms in the system.
 784             constraintErrorTolerance: Distance tolerance for constraints as a
 785                 fraction of the constrained distance.
 786             ewaldErrorTolerance: Ewald error tolerance for a periodic system.
 787             
 788             nonbondedMethodPeriodic: Nonbonded method to use during the
 789                 calculation of long range interactions for a periodic system.
 790             nonbondedMethodNonPeriodic: Nonbonded method to use during the
 791                 calculation of long range interactions for a non-periodic system.
 792             nonbondedCutoff: Cutoff distance to use for long range interactions
 793                 in both perioidic non-periodic systems.
 794             
 795             hydrogenMassRepartioning: Use hydrogen mass repartioning. It
 796                 increases the mass of the hydrogen atoms attached to the heavy
 797                 atoms and decreasing the mass of the bonded heavy atom to
 798                 maintain constant system mass. This allows the use of larger
 799                 integration step size (4 fs) during a simulation.
 800             hydrogenMass: Hydrogen mass to use during repartioning.
 801             
 802             removeCMMotion: Remove all center of mass motion at every time step.
 803             rigidWater: Keep water rigid during a simulation. This is determined
 804                 automatically based on the value of 'constraints' parameter.
 805             
 806     --waterBox <yes or no>  [default: no]
 807         Add water box.
 808     --waterBoxParams <Name,Value,..>  [default: auto]
 809         A comma delimited list of parameter name and value pairs for adding
 810         a water box.
 811         
 812         The supported parameter names along with their default values are
 813         are shown below:
 814             
 815             model, tip3p [ Possible values: tip3p, spce, tip4pew, tip5p or
 816                 swm4ndp ]
 817             mode, Padding  [ Possible values: Size or Padding ]
 818             padding, 1.0
 819             size, None  [ Possible value: xsize ysize zsize ]
 820             shape, cube  [ Possible values: cube, dodecahedron, or octahedron ]
 821             ionPositive, Na+ [ Possible values: Li+, Na+, K+, Rb+, or Cs+ ]
 822             ionNegative, Cl- [ Possible values: Cl-, Br-, F-, or I- ]
 823             ionicStrength, 0.0
 824             
 825         A brief description of parameters is provided below:
 826             
 827             model: Water model to use for adding water box. The van der
 828                 Waals radii and atomic charges are determined using the
 829                 specified water forcefield. You must specify an appropriate
 830                 water forcefield. No validation is performed.
 831             mode: Specify the size of the waterbox explicitly or calculate it
 832                 automatically for a macromolecule along with adding padding
 833                 around ther macromolecule.
 834             padding: Padding around a macromolecule in nanometers for filling
 835                 box with water. It must be specified during 'Padding' value of
 836                 'mode' parameter.
 837             size: A space delimited triplet of values corresponding to water
 838                 size in nanometers. It must be specified during 'Size' value of
 839                 'mode' parameter.
 840             ionPositive: Type of positive ion to add during the addition of a
 841                 water box.
 842             ionNegative: Type of negative ion to add during the addition of a
 843                 water box.
 844             ionicStrength: Total concentration of both positive and negative
 845                 ions to add excluding the ions added to neutralize the system
 846                 during the addition of a water box.
 847             
 848     -w, --workingdir <dir>
 849         Location of working directory which defaults to the current directory.
 850 
 851 Examples:
 852     To perform energy minimization for a macromolecule in a PDB file until the
 853     energy is converged, writing information to log file every 100 steps and 
 854     generate a PDB file for the minimized system, type:
 855 
 856         % OpenMMPerformMinimization.py -i Sample13.pdb
 857 
 858     To run the first example for writing information to both stdout and log file
 859      every 100 steps and generate various output files, type:
 860 
 861         % OpenMMPerformMinimization.py -i Sample13.pdb --outputParams
 862           "minimizationDataStdout, yes"
 863 
 864     To run the first example for performing OpenMM simulation using multi-
 865     threading employing all available CPUs on your machine and generate various
 866     output files, type:
 867 
 868         % OpenMMPerformMinimization.py -i Sample13.pdb
 869           --platformParams "threads,0"
 870 
 871     To run the first example for performing OpenMM simulation using CUDA platform
 872     on your machine and generate various output files, type:
 873 
 874         % OpenMMPerformMinimization.py -i Sample13.pdb -p CUDA
 875 
 876     To run the second example for a marcomolecule in a complex with a small
 877     molecule and generate various output files, type:
 878 
 879         % OpenMMPerformMinimization.py -i Sample13.pdb -s Sample13Ligand.sdf
 880           --platformParams "threads,0"
 881 
 882     To run the second example by adding a water box to the system and generate
 883     various output files, type:
 884 
 885         % OpenMMPerformMinimization.py -i Sample13.pdb --waterBox yes
 886           --platformParams "threads,0"
 887 
 888     To run the second example for a marcomolecule in a complex with a small
 889     molecule by adding a water box to the system and generate various output
 890     files, type:
 891 
 892         % OpenMMPerformMinimization.py -i Sample13.pdb -s Sample13Ligand.sdf
 893           --waterBox yes --platformParams "threads,0"
 894 
 895     To run the second example by freezing CAlpha atoms in a macromolecule without
 896     using any system constraints to avoid any issues with the freezing of the same atoms
 897     and generate various output files, type:
 898 
 899         % OpenMMPerformMinimization.py -i Sample13.pdb
 900           --freezeAtoms yes --freezeAtomsParams "selection,CAlphaProtein"
 901           --systemParams "constraints, None"
 902           --platformParams "threads,0"
 903 
 904         % OpenMMPerformMinimization.py -i Sample13.pdb
 905           --freezeAtoms yes --freezeAtomsParams "selection,CAlphaProtein"
 906           --systemParams "constraints, None"
 907           --platformParams "threads,0" --waterBox yes
 908 
 909     To run the second example by restrainting CAlpha atoms in a macromolecule and
 910     and generate various output files, type:
 911 
 912         % OpenMMPerformMinimization.py -i Sample13.pdb
 913           --restraintAtomsParams "selection,CAlphaProtein"
 914           --platformParams "threads,0"
 915 
 916         % OpenMMPerformMinimization.py -i Sample13.pdb
 917           --restraintAtomsParams "selection,CAlphaProtein"
 918           --platformParams "threads,0"
 919           --waterBox yes
 920 
 921     To run the second example by specifying explict values for various parametres
 922     and generate various output files, type:
 923 
 924         % OpenMMPerformMinimization.py -i Sample13.pdb
 925           -f " biopolymer,amber14-all.xml,smallMolecule, openff-2.2.1,
 926           water,amber14/tip3pfb.xml" --waterBox yes
 927           --outputParams "minimizationDataSteps, 100, minimizationDataStdout,
 928           yes,minimizationDataLog,yes"
 929           -p CPU --platformParams "threads,0"
 930           --simulationParams "minimizationMaxSteps,500,
 931           minimizationTolerance, 0.24"
 932           --systemParams "constraints,BondsInvolvingHydrogens,
 933           nonbondedMethodPeriodic,PME,nonbondedMethodNonPeriodic,NoCutoff,
 934           hydrogenMassRepartioning, yes"
 935 
 936 Author:
 937     Manish Sud(msud@san.rr.com)
 938 
 939 See also:
 940     OpenMMPrepareMacromolecule.py, OpenMMPerformMDSimulation.py,
 941     OpenMMPerformSimulatedAnnealing.py
 942 
 943 Copyright:
 944     Copyright (C) 2025 Manish Sud. All rights reserved.
 945 
 946     The functionality available in this script is implemented using OpenMM, an
 947     open source molecuar simulation package.
 948 
 949     This file is part of MayaChemTools.
 950 
 951     MayaChemTools is free software; you can redistribute it and/or modify it under
 952     the terms of the GNU Lesser General Public License as published by the Free
 953     Software Foundation; either version 3 of the License, or (at your option) any
 954     later version.
 955 
 956 """
 957 
 958 if __name__ == "__main__":
 959     main()