MayaChemTools

    1 #!/bin/env python
    2 #
    3 # File: OpenFECalculateRelativeBindingFreeEnergySepTop.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 OpenFE, an
    9 # open source package for alchemical free energy calculations.
   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 logging
   36 import pathlib
   37 import pandas as pd
   38 
   39 # OpenFE imports...
   40 try:
   41     import openfe
   42     from openfe.protocols.openmm_septop import SepTopProtocol
   43 except ImportError as ErrMsg:
   44     sys.stderr.write("\nFailed to import OpenFE related module/package: %s\n" % ErrMsg)
   45     sys.stderr.write("Check/update your OpenFE environment and try again.\n\n")
   46     sys.exit(1)
   47 
   48 # RDKit imports...
   49 try:
   50     from rdkit import rdBase
   51 except ImportError as ErrMsg:
   52     sys.stderr.write("\nFailed to import RDKit module/package: %s\n" % ErrMsg)
   53     sys.stderr.write("Check/update your RDKit environment and try again.\n\n")
   54     sys.exit(1)
   55 
   56 # MayaChemTools imports...
   57 sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), "..", "lib", "Python"))
   58 try:
   59     from docopt import docopt
   60     import MiscUtil
   61     import OpenFEUtil
   62 except ImportError as ErrMsg:
   63     sys.stderr.write("\nFailed to import MayaChemTools module/package: %s\n" % ErrMsg)
   64     sys.stderr.write("Check/update your MayaChemTools environment and try again.\n\n")
   65     sys.exit(1)
   66 
   67 ScriptName = os.path.basename(sys.argv[0])
   68 Options = {}
   69 OptionsInfo = {}
   70 
   71 
   72 def main():
   73     """Start execution of the script."""
   74 
   75     MiscUtil.PrintInfo(
   76         "\n%s (OpenFE v%s; OpenMM v%s; RDKit v%s; MayaChemTools v%s; %s): Starting...\n"
   77         % (
   78             ScriptName,
   79             openfe.version("openfe"),
   80             openfe.version("openmm"),
   81             rdBase.rdkitVersion,
   82             MiscUtil.GetMayaChemToolsVersion(),
   83             time.asctime(),
   84         )
   85     )
   86 
   87     (WallClockTime, ProcessorTime) = MiscUtil.GetWallClockAndProcessorTime()
   88 
   89     # Retrieve command line arguments and options...
   90     RetrieveOptions()
   91 
   92     if Options["--list"]:
   93         ProcessListOption()
   94     else:
   95         # Process and validate command line arguments and options...
   96         ProcessOptions()
   97 
   98         # Perform actions required by the script...
   99         CalculateRelativeBindingFreeEnergy()
  100 
  101     MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName)
  102     MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime))
  103 
  104 
  105 def CalculateRelativeBindingFreeEnergy():
  106     """Calculate relative binding free energy using separated topologies."""
  107 
  108     # Process input files...
  109     MacroMol, Mols = ProcessInputFiles()
  110 
  111     # Process molecule names...
  112     MolPairs = ProcessMoleculeNames(Mols)
  113 
  114     # Check for missing partial charges...
  115     CheckMissingPartialCharges(MolPairs)
  116 
  117     # Initialize RBFE protocols...
  118     RBFEProtocol = InitializeSeparatedTopologyProtocol()
  119 
  120     # Initialize solvent...
  121     Solvent = InitializeSolventComponent()
  122 
  123     # Setup transformations...
  124     MolAToMolBTransformations = SetupTransformations(MolPairs, MacroMol, Solvent, RBFEProtocol)
  125 
  126     # Setup protocol DAGs...
  127     MolAToMolBProtocolDAGs = SetupProtocolDAGs(MolAToMolBTransformations)
  128 
  129     # Execute protocol DAGs and gather results...
  130     MolAToMolBProtocolResults = ExecuteProtocolDAGsAndGatherResults(MolAToMolBTransformations, MolAToMolBProtocolDAGs)
  131 
  132     # Process protocol results...
  133     ProcessProtocolResults(MolAToMolBTransformations, MolAToMolBProtocolResults)
  134 
  135 
  136 def InitializeSeparatedTopologyProtocol():
  137     """Initialize separated toplology protocol."""
  138 
  139     MiscUtil.PrintInfo("\nInitializing separated topologies protocol...")
  140 
  141     RBFESettings = OpenFEUtil.SetupRelativeFreeEnergySeparatedTopologySettings(
  142         "-r, --rbfeParams", OptionsInfo["RBFEParams"]
  143     )
  144     RBFEProtocol = OpenFEUtil.InitializeRelativeFreeEngerySeparatedTopologyProtocol(RBFESettings)
  145 
  146     return RBFEProtocol
  147 
  148 
  149 def InitializeSolventComponent():
  150     """Initialize solvent component."""
  151 
  152     SolventParams = OptionsInfo["SolventParams"]
  153     MiscUtil.PrintInfo(
  154         "\nInitializing solvent component (PositiveIon: %s; NegativeIon: %s; Neutralize: %s; IonConcentration: %s)..."
  155         % (
  156             SolventParams["PositiveIon"],
  157             SolventParams["NegativeIon"],
  158             SolventParams["Neutralize"],
  159             SolventParams["IonConcentration"],
  160         )
  161     )
  162 
  163     Solvent = OpenFEUtil.InitializeSolventComponent(SolventParams)
  164 
  165     return Solvent
  166 
  167 
  168 def SetupTransformations(MolPairs, MacroMol, Solvent, RBFEProtocol):
  169     """Set up transformations for moleule pairs."""
  170 
  171     MiscUtil.PrintInfo("\nSetting up transformations (Count: %s)..." % (int(len(MolPairs) / 2)))
  172 
  173     MolAToMolBTransformations = []
  174     for Index in range(0, len(MolPairs), 2):
  175         MolA = MolPairs[Index]
  176         MolB = MolPairs[Index + 1]
  177 
  178         # Setup chemical systems...
  179         MolAComplexSolventSystem = OpenFEUtil.InitializeChemicalSystem(
  180             SmallMol=MolA, MacroMol=MacroMol, Solvent=Solvent, Name="%s_Complex_Solvent" % MolA.name
  181         )
  182         MolBComplexSolventSystem = OpenFEUtil.InitializeChemicalSystem(
  183             SmallMol=MolB, MacroMol=MacroMol, Solvent=Solvent, Name="%s_Complex_Solvent" % MolB.name
  184         )
  185 
  186         # Setup MolAComplexSolvent to MolBComplexSolvent transformation...
  187         TransformationName = "%s_To_%s_Complex_Solvent" % (MolA.name, MolB.name)
  188         MolAToMolBComplexSolventTransformation = OpenFEUtil.InitializeTransformation(
  189             StateA=MolAComplexSolventSystem,
  190             StateB=MolBComplexSolventSystem,
  191             Mapping=None,
  192             Protocol=RBFEProtocol,
  193             Name=TransformationName,
  194             Validate=False,
  195         )
  196         MolAToMolBTransformations.append(MolAToMolBComplexSolventTransformation)
  197 
  198     # Write out transformatios...
  199     WriteTransformations(MolAToMolBTransformations)
  200 
  201     return MolAToMolBTransformations
  202 
  203 
  204 def WriteTransformations(MolAToMolBTransformations):
  205     """Write out transformations."""
  206 
  207     TransformationsOutDirPath = pathlib.Path(OptionsInfo["TransformationsOutDirPath"])
  208 
  209     MiscUtil.PrintInfo(
  210         "Writing transformations files (Files: *.json; Count: %s; Subdirectory: %s)..."
  211         % (len(MolAToMolBTransformations), OptionsInfo["TransformationsOutDir"])
  212     )
  213 
  214     for Transformation in MolAToMolBTransformations:
  215         TransformationFilePath = TransformationsOutDirPath.joinpath("%s.json" % Transformation.name)
  216         Transformation.dump(TransformationFilePath)
  217 
  218 
  219 def SetupProtocolDAGs(MolAToMolBTransformations):
  220     """Setup protocol Directed Acyclic Graphs (DAGs) for each transformation to
  221     to perform calculations.
  222     """
  223 
  224     MiscUtil.PrintInfo("\nSetting up protocol DAGs (Count: %s)..." % len(MolAToMolBTransformations))
  225 
  226     MolAToMolBProtocolDAGs = []
  227     for Transformation in MolAToMolBTransformations:
  228         ProtocolDAG = OpenFEUtil.InitializeProtocolDAG(Transformation, Name=Transformation.name)
  229         MolAToMolBProtocolDAGs.append(ProtocolDAG)
  230 
  231     return MolAToMolBProtocolDAGs
  232 
  233 
  234 def ExecuteProtocolDAGsAndGatherResults(MolTransformations, MolProtocolDAGs):
  235     """Execute protocol DAGs and gather results."""
  236 
  237     ResultsSharedOutDirPath = OptionsInfo["ResultsOutDirPath"]
  238     ResultsScratchOutDirPath = OptionsInfo["ResultsScratchOutDirPath"]
  239     ExecuteDAGParams = OptionsInfo["ExecuteDAGParams"]
  240 
  241     MolProtocolResults = OpenFEUtil.ExecuteProtocolDAGsAndGatherResults(
  242         MolTransformations,
  243         MolProtocolDAGs,
  244         ResultsSharedOutDirPath,
  245         ResultsScratchOutDirPath,
  246         KeepShared=ExecuteDAGParams["KeepShared"],
  247         KeepScratch=ExecuteDAGParams["KeepScratch"],
  248         NRetries=ExecuteDAGParams["NRetries"],
  249         WriteResults=True,
  250     )
  251 
  252     return MolProtocolResults
  253 
  254 
  255 def ProcessProtocolResults(MolAToMolBTransformations, MolAToMolBProtocolResults):
  256     """Process protocol results."""
  257 
  258     ResultFileParams = OptionsInfo["ResultFileParams"]
  259 
  260     ResultFile = "%s_RBFE_Results.%s" % (OptionsInfo["OutfilePrefix"], ResultFileParams["Ext"])
  261     ResultFilePath = os.path.join(OptionsInfo["OutfileDirPath"], ResultFile)
  262     MiscUtil.PrintInfo("\nWriting %s..." % ResultFile)
  263 
  264     Precision = ResultFileParams["Precision"]
  265 
  266     ResultData = []
  267     for Index in range(0, len(MolAToMolBProtocolResults), 1):
  268         MolAToMolBProtocolResult = MolAToMolBProtocolResults[Index]
  269 
  270         # Setup mol names using transformation...
  271         MolAToMolBTransformation = MolAToMolBTransformations[Index]
  272         MolA = MolAToMolBTransformation.stateA.components["ligand"]
  273         MolB = MolAToMolBTransformation.stateB.components["ligand"]
  274         MolAName = MolA.name
  275         MolBName = MolB.name
  276 
  277         if MolAToMolBProtocolResult is None:
  278             DeltaDeltaGBinding = "NA"
  279             DeltaDeltaGBindingUncertainty = "NA"
  280         else:
  281             # Setup binding value without the units...
  282             DeltaDeltaGBinding = MolAToMolBProtocolResult.get_estimate()
  283             DeltaDeltaGBinding = "%.*f" % (Precision, DeltaDeltaGBinding.m)
  284 
  285             # Setup uncertainty value without the units...
  286             DeltaDeltaGBindingUncertainty = MolAToMolBProtocolResult.get_uncertainty()
  287             DeltaDeltaGBindingUncertainty = "%.*f" % (Precision, DeltaDeltaGBindingUncertainty.m)
  288 
  289         ResultData.append([MolAName, MolBName, DeltaDeltaGBinding, DeltaDeltaGBindingUncertainty])
  290 
  291     ResultDF = pd.DataFrame(
  292         ResultData,
  293         columns=["MolAName", "MolBName", "DeltaDeltaG (MolA->MolB; RBFE) (kcal/mol)", "Uncertainty (kcal/mol)"],
  294     )
  295     ResultDF.to_csv(ResultFilePath, sep=ResultFileParams["Delim"], lineterminator="\n", index=False)
  296 
  297 
  298 def ProcessInputFiles():
  299     """Process input files."""
  300 
  301     # Read PDB file...
  302     MiscUtil.PrintInfo("\nReading PDB file %s..." % OptionsInfo["Infile"])
  303     MacroMol = OpenFEUtil.ReadPDBFile(OptionsInfo["InfilePath"], Name=OptionsInfo["InfileRoot"])
  304 
  305     # Read small molecule input file...
  306     MiscUtil.PrintInfo("\nReading small molecule file %s..." % OptionsInfo["SmallMolFile"])
  307     Mols, MolCount, ValidMolCount = OpenFEUtil.ReadAndValidateMolecules(
  308         OptionsInfo["SmallMolFilePath"], **OptionsInfo["SmallMolFileParams"]
  309     )
  310 
  311     MiscUtil.PrintInfo("\nTotal number of molecules: %d" % MolCount)
  312     MiscUtil.PrintInfo("Number of valid molecules: %d" % ValidMolCount)
  313     MiscUtil.PrintInfo("Number of ignored molecules: %d" % (MolCount - ValidMolCount))
  314 
  315     if ValidMolCount == 0:
  316         MiscUtil.PrintInfo("")
  317         MiscUtil.PrintError("No valid molecules found in small molecule input file.\n")
  318 
  319     if ValidMolCount < 2:
  320         MiscUtil.PrintInfo("")
  321         MiscUtil.PrintError("Small molecule Input file must contain at least 2 molecules.\n")
  322 
  323     return (MacroMol, Mols)
  324 
  325 
  326 def ProcessMoleculeNames(Mols):
  327     """Process molecule names."""
  328 
  329     MolPairs = OpenFEUtil.ProcessMoleculePairs(Mols, OptionsInfo["MoleculePairsList"])
  330 
  331     return MolPairs
  332 
  333 
  334 def CheckMissingPartialCharges(Mols):
  335     """Check missing partial charges for small molecules."""
  336 
  337     MiscUtil.PrintInfo("\nChecking missing partial charges for specidfed small molecule pairs...")
  338 
  339     MissingChargesMolCount = OpenFEUtil.GetMissingPartialChargesMolCount(Mols)
  340     MiscUtil.PrintInfo("Number of molecules with missing partial charges: %s" % MissingChargesMolCount)
  341 
  342     if MissingChargesMolCount == 0:
  343         return
  344 
  345     if re.match("^Stop$", OptionsInfo["MissingChargeMode"], re.I):
  346         MiscUtil.PrintInfo("")
  347         MiscUtil.PrintError(
  348             'The small molecule input file contains molecules with missing partial charges. The execution of the script has been terminated for "Stop" value of "--missingChargedMode" option. You may continue the execution of the script by specifying "Calculate" value for "--missingChargedMode" option.\n\nThe missing charges will be automatically calculated by OpenFE RelativeHybridTopologyProtocol module during the calculation of RBFE. You may control the calculation of partial charges by specifying values for partialCharge* parameters using "--rbfeParams" option.  Alternatively, you may employ the OpenFECalculatePartialCharges.py script to calculate partial charges and use the small molecule input file containing charges to calculate RBFE.\n'
  349         )
  350     else:
  351         MiscUtil.PrintInfo("")
  352         MiscUtil.PrintWarning(
  353             'The small molecule input file contains molecules with missing partial charges. The missing charges will be automatically calculated by OpenFE RelativeHybridTopologyProtocol module during the calculation of RBFE. You may control the calculation of partial charges by specifying values for partialCharge* parameters using "--rbfeParams" option. Alternatively, you may employ the OpenFECalculatePartialCharges.py script to calculate partial charges and use the small molecule input file containing charges to calculate RBFE.\n'
  354         )
  355 
  356 
  357 def ProcessOutfilePrefixOption():
  358     """Process outfile prefix option."""
  359 
  360     OutfilePrefix = Options["--outfilePrefix"]
  361 
  362     if re.match("^auto$", OutfilePrefix, re.I):
  363         OutfilePrefix = OptionsInfo["SmallMolFileRoot"]
  364 
  365     OptionsInfo["OutfilePrefix"] = OutfilePrefix
  366 
  367 
  368 def ProcessOutfileDirOption():
  369     """Process outfile directory Option."""
  370 
  371     # Setup output directory...
  372     OutfileDir = Options["--outfileDir"]
  373     OutfileDirPath = os.path.abspath(OutfileDir)
  374     if not os.path.exists(OutfileDir):
  375         MiscUtil.PrintInfo("\nCreating output directory %s..." % (OutfileDir))
  376         os.mkdir(OutfileDirPath)
  377     OptionsInfo["OutfileDir"] = OutfileDir
  378     OptionsInfo["OutfileDirPath"] = OutfileDirPath
  379 
  380     # Setup a transformations subdirectory...
  381     TransformationsOutDir = "Transformations"
  382     TransformationsOutDirPath = os.path.join(OptionsInfo["OutfileDirPath"], TransformationsOutDir)
  383     if not os.path.exists(TransformationsOutDirPath):
  384         os.mkdir(TransformationsOutDirPath)
  385     OptionsInfo["TransformationsOutDir"] = TransformationsOutDir
  386     OptionsInfo["TransformationsOutDirPath"] = TransformationsOutDirPath
  387 
  388     # Setup a results subdirectory...
  389     ResultsOutDir = "Results"
  390     ResultsOutDirPath = os.path.join(OptionsInfo["OutfileDirPath"], ResultsOutDir)
  391     if not os.path.exists(ResultsOutDirPath):
  392         os.mkdir(ResultsOutDirPath)
  393     OptionsInfo["ResultsOutDir"] = ResultsOutDir
  394     OptionsInfo["ResultsOutDirPath"] = ResultsOutDirPath
  395 
  396     # Use results subdirectory for scratch results...
  397     OptionsInfo["ResultsScratchOutDir"] = ResultsOutDir
  398     OptionsInfo["ResultsScratchOutDirPath"] = ResultsOutDirPath
  399 
  400 
  401 def ProcessListOption():
  402     """Process list protocol settings option."""
  403 
  404     RBFESettings = SepTopProtocol.default_settings()
  405 
  406     MiscUtil.PrintInfo("\nListing RBFE seperated topology settings...")
  407     OpenFEUtil.ListOpenFESettings(RBFESettings)
  408 
  409 
  410 def ConfigureLogging():
  411     """Configure logging."""
  412 
  413     OptionsInfo["LoggingLevel"] = Options["--loggingLevel"]
  414 
  415     if re.match("^Error$", OptionsInfo["LoggingLevel"], re.I):
  416         LoggingLevel = logging.ERROR
  417     elif re.match("^Warning$", OptionsInfo["LoggingLevel"], re.I):
  418         LoggingLevel = logging.WARNING
  419     else:
  420         LoggingLevel = logging.INFO
  421 
  422     logging.basicConfig(format="%(levelname)s: %(message)s", level=LoggingLevel)
  423 
  424     # Turn warnings issued by warnings.warn() into log message to avoid display
  425     # of a stack trace...
  426     logging.captureWarnings(True)
  427 
  428 
  429 def ProcessOptions():
  430     """Process and validate command line arguments and options."""
  431 
  432     MiscUtil.PrintInfo("Processing options...")
  433 
  434     # Validate options...
  435     ValidateOptions()
  436 
  437     # Configure logging...
  438     ConfigureLogging()
  439 
  440     OptionsInfo["Infile"] = Options["--infile"]
  441     OptionsInfo["InfilePath"] = os.path.abspath(OptionsInfo["Infile"])
  442     FileDir, FileName, FileExt = MiscUtil.ParseFileName(OptionsInfo["Infile"])
  443     OptionsInfo["InfileRoot"] = FileName
  444 
  445     OptionsInfo["SmallMolFile"] = Options["--smallMolFile"]
  446     OptionsInfo["SmallMolFilePath"] = os.path.abspath(OptionsInfo["SmallMolFile"])
  447     FileDir, FileName, FileExt = MiscUtil.ParseFileName(OptionsInfo["SmallMolFile"])
  448     OptionsInfo["SmallMolFileRoot"] = FileName
  449 
  450     ParamsDefaultInfoOverride = {"RemoveHydrogens": False}
  451     OptionsInfo["SmallMolFileParams"] = MiscUtil.ProcessOptionInfileParameters(
  452         "--smallMolFileParams",
  453         Options["--smallMolFileParams"],
  454         InfileName=Options["--smallMolFile"],
  455         ParamsDefaultInfo=ParamsDefaultInfoOverride,
  456     )
  457 
  458     OptionsInfo["ExecuteDAGParams"] = OpenFEUtil.ProcessOptionOpenFEExecuteDAGParameters(
  459         "--executeDAGParams", Options["--executeDAGParams"]
  460     )
  461 
  462     OptionsInfo["LoggingLevel"] = Options["--loggingLevel"]
  463 
  464     OptionsInfo["MissingChargeMode"] = OpenFEUtil.ProcessOptionOpenFEMissingChargeMode(
  465         "--missingChargeMode", Options["--missingChargeMode"]
  466     )
  467 
  468     OptionsInfo["MoleculePairs"] = Options["--moleculePairs"]
  469     OptionsInfo["MoleculePairsList"] = OpenFEUtil.ProcessOptionOpenFEMoleculePairs(
  470         "--moleculePairs", Options["--moleculePairs"]
  471     )
  472     OptionsInfo["MoleculePairsMolList"] = None
  473 
  474     OptionsInfo["ResultFileParams"] = OpenFEUtil.ProcessOptionOpenFEResultFileParameters(
  475         "--resultFileParams", Options["--resultFileParams"]
  476     )
  477 
  478     ParamsDefaultInfoOverride = {"EngineComputePlatform": "CPU"}
  479     OptionsInfo["RBFEParams"] = OpenFEUtil.ProcessOptionOpenFERelativeFreeEnergySeparatedTopologyParameters(
  480         "--rbfeParams", Options["--rbfeParams"], ParamsDefaultInfo=ParamsDefaultInfoOverride
  481     )
  482 
  483     OptionsInfo["SolventParams"] = OpenFEUtil.ProcessOptionOpenFESolventParameters(
  484         "--solventParams", Options["--solventParams"]
  485     )
  486 
  487     ProcessOutfilePrefixOption()
  488     ProcessOutfileDirOption()
  489 
  490     OptionsInfo["Overwrite"] = Options["--overwrite"]
  491 
  492     # Track top level working directory...
  493     OptionsInfo["TopWorkingDir"] = os.getcwd()
  494 
  495 
  496 def RetrieveOptions():
  497     """Retrieve command line arguments and options."""
  498 
  499     # Get options...
  500     global Options
  501     Options = docopt(_docoptUsage_)
  502 
  503     # Set current working directory to the specified directory...
  504     WorkingDir = Options["--workingdir"]
  505     if WorkingDir:
  506         os.chdir(WorkingDir)
  507 
  508     # Handle examples option...
  509     if "--examples" in Options and Options["--examples"]:
  510         MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_))
  511         sys.exit(0)
  512 
  513 
  514 def ValidateOptions():
  515     """Validate option values."""
  516 
  517     MiscUtil.ValidateOptionFilePath("-i, --infile", Options["--infile"])
  518     MiscUtil.ValidateOptionFileExt("-i, --infile", Options["--infile"], "pdb cif")
  519 
  520     MiscUtil.ValidateOptionFilePath("-s, --smallMolFile", Options["--smallMolFile"])
  521     MiscUtil.ValidateOptionFileExt("-s, --smallMolFile", Options["--smallMolFile"], "sdf sd")
  522 
  523     MiscUtil.ValidateOptionDirPath("-o, --outfileDir", Options["--outfileDir"])
  524     MiscUtil.ValidateOptionsOutputDirOverwrite(
  525         "-o, --outfileDir", Options["--outfileDir"], "--overwrite", Options["--overwrite"]
  526     )
  527 
  528     MiscUtil.ValidateOptionTextValue("--loggingLevel", Options["--loggingLevel"], "Info Warning Error")
  529 
  530     MiscUtil.ValidateOptionTextValue("--missingChargeMode", Options["--missingChargeMode"], "Calculate Stop")
  531 
  532     MoleculePairs = Options["--moleculePairs"]
  533     if not re.match("^auto$", MoleculePairs, re.I):
  534         MoleculePairsList = MoleculePairs.split(",")
  535         if len(MoleculePairsList) % 2:
  536             MiscUtil.PrintError(
  537                 'The number of comma delimited values, %d, specified using "--moleculePairs" option must be an even number.'
  538                 % (len(MoleculePairsList))
  539             )
  540 
  541 
  542 # Setup a usage string for docopt...
  543 _docoptUsage_ = """
  544 OpenFECalculateRelativeBindingFreeEnergySepTop.py - Calculate relative binding free energy
  545 
  546 Usage:
  547     OpenFECalculateRelativeBindingFreeEnergySepTop.py [--executeDAGParams <Name,Value,..>] [--loggingLevel <Info, Warning or Error>]
  548                                                       [--missingChargeMode <Calculate or Stop>] [--moleculePairs <MolName1,MolName2,..>]
  549                                                       [--outfilePrefix <text>] [--overwrite] [--rbfeParams <Name,Value,...>] [--resultFileParams <Name,Value,..>]
  550                                                       [--smallMolFileParams <Name,Value,...> ] [--solventParams <Name,Value,...>]
  551                                                       [-w <dir>] -i <infile> -s <smallmolfile> -o <outifiledir>
  552     OpenFECalculateRelativeBindingFreeEnergySepTop.py -l | --list
  553     OpenFECalculateRelativeBindingFreeEnergySepTop.py -h | --help | -e | --examples
  554 
  555 Description:
  556     Calculate Relative Binding Free Energy (RBFE) for a pair of molecules in a
  557     small molecule input file using a Separated Topologies (SepTop) [ Ref 183 ]
  558     approach.
  559 
  560     You may calculate RBFEs for any arbitrary pairs of molecules. The SepTop
  561     protocol calculates RBFE by performing two ABFE calculations simultaneously
  562     in opposite directions. No atom mapping is required between pairs of molecules
  563     to create hybrid topology. Consequently, the SepTop based RBFEs calculations
  564     may be used for scaffold hopping.
  565 
  566     A brief description of the SepTop protocol, taken from OpenFE documenation is
  567     provided below:
  568 
  569         The relative binding free energy is obtained through a thermodynamic
  570         cycle. The molecules are transformed into each other both in solvent,
  571         giving ddG (solvent), and in the complex, giving ddG (complex), which
  572         allows the calculation of the relative binding free energy, ddG. Each
  573         molecule is represented with its own set of coordinates, meaning that
  574         the interactions of all atoms of one molecule are turned off while
  575         simultaneously turning on the interactions of all atoms of the other
  576         molecule. Therefore, restraints are required: Molecules are restrained
  577         to the protein in the complex states using orientational (Boresch-style)
  578         restraints; in the solvent states molecules are restrained to remain
  579         apart from each other using a single harmonic distance restraint between
  580         the molecules.
  581         
  582         The coulombic interactions of the molecule are fully turned off
  583         (annihilated), while the Lennard-Jones interactions are decoupled,
  584         meaning the intermolecular interactions are turned off, while keeping
  585         the intramolecular Lennard-Jones interactions.
  586 
  587     The input file must contain a macromolecule already prepared for simulation.
  588     The preparation of the macromolecule for a simulation generally involves the
  589     following tasks: identification and replacement of non-standard residues;
  590     addition of missing residues; addition of missing heavy atoms; addition of
  591     missing hydrogens.
  592 
  593     In addition, the small molecule input file must contain molecules already
  594     prepared for simulation. It must contain appropriate 3D coordinates relative
  595     to the macromolecule along with no missing hydrogens.
  596 
  597         Protocol repeats, 3
  598         
  599         Time step size: 4.0 femtosecond
  600         
  601         Complex equilibration phase:
  602         
  603         Max minimization steps: 5,000
  604         NVT equilibration length: 0.1 nanosecond
  605         NPT equilibration length: 0.1 nanosecond
  606         NPT length: 2.0 nanosecond
  607         
  608         Complex production phase:
  609         
  610         Max minimization steps: 5,000
  611         NPT equilibration length: 1.0 nanosecond
  612         NPT length: 10.0 nanosecond
  613 
  614         Solvent equilibration phase:
  615         
  616         Max minimization steps: 5,000
  617         NVT equilibration length: 0.1 nanosecond
  618         NPT equilibration length: 0.1 nanosecond
  619         NPT length: 2.0 nanosecond
  620         
  621         Solvent production phase:
  622         
  623         Max minimization steps: 5,000
  624         NPT equilibration length: 1.0 nanosecond
  625         NPT length: nanosecond 10.0 nanosecond
  626 
  627     Each complex and solvent simulation, by default, may run for 13.2 and 13.2
  628     nanosecond respectively, for a total of 26.4 nanoseconds. The total MD
  629     simulation time for correspond to 79.2 nanosecond to repeat the protocol
  630     3 times for the complex and solvent simulations.
  631 
  632     The supported macromolecule input file formats are:  PDB (.pdb) and
  633     CIF (.cif)
  634 
  635     The supported small molecule input file format are : SD (.sdf, .sd)
  636 
  637     Possible outfile prefix:
  638         
  639         <OutfilePrefix> or <SmallMolFileRoot>
  640         
  641     Possible output directories:
  642         
  643         <OutfileDir>
  644         
  645         <OutfileDir>/Transformations
  646         <OutfileDir>/Results
  647         
  648     Possible output files and directories under <OutfileDir>:
  649         
  650         <OutfilePrefix>_RBFE_Results.<csv or tsv>
  651         
  652         ... ... ...
  653 
  654         Transformations/<MolAName>_To_<MolBName>_Complex_Solvent.json
  655         ... ... ...
  656         
  657         Results/<MolAName>_To_<MolBName>_Complex_Solvent_Results.json
  658         Results/shared_SepTopComplexRunUnit-*/
  659         Results/shared_SepTopComplexSetupUnit-*/
  660         Results/shared_SepTopSolventRunUnit-*/
  661         Results/shared_SepTopSolventSetupUnit-*/
  662         ... ... ...
  663 
  664 Options:
  665     -e, --examples
  666         Print examples.
  667     --executeDAGParams <Name,Value,..>  [default: auto]
  668         A comma delimited list of parameter name and value pairs for executing
  669         protocol DAGs (Directed Acyclic Graph) to run RBFE calculations.
  670         
  671         The supported parameter names along with their default values are
  672         are shown below:
  673             
  674             keepShared, yes  [ Possible values: yes or no ]
  675             keepScratch, no  [ Possible values: yes or no ]
  676             nRetries, 2  [ Possible values: >= 0. A value of 0 implies only
  677                 1 try. ]
  678             
  679         A brief description of parameters is provided below:
  680             
  681             keepShared: Keep shared directories after the execution of DAG.
  682             keepScratch: Keep scratch directories after the execution of DAG.
  683             nRetries: Number of times to attempt the execution.
  684             
  685     -h, --help
  686         Print this help message.
  687     -i, --infile <infile>
  688         Input file name containing a macromolecule.
  689     -l, --list
  690         List default RBFE protocol settings provided by OpenFE module
  691         RelativeHybridTopologyProtocol.
  692     --loggingLevel <Info, Warning or Error>  [default: Error]
  693         Logging level to configure the 'root logger' via logging.basicConfig()
  694         function. The default logging level is changed from 'logging.INFO' to
  695         'logging.ERROR'. Otherwise, OpenFE and its associated modules
  696         may generate a lot of informational messages.
  697     --missingChargeMode <Calculate or Stop>  [default: Stop]
  698         Calculate missing partial charges for molecules before running RBFE
  699         calculations or terminate the execution of the script. The missing
  700         partial charges will be automatically calculated by OpenFE module
  701         RelativeHybridTopologyProtocol during the calculation of RBFE. You
  702         may control the calculation of partial charges by specifying values for
  703         partialCharge* parameters using '--rbfeParams' option.
  704     -m, --moleculePairs <MolName1,MolName2,..>  [default: auto]
  705         A comma delimited list of molecule name pairs for calculating RBFEs.
  706         Default: the names of the first and second molecule in small molecule
  707         input file.
  708     -o, --outfileDir <outfiledir>
  709         Output directory.
  710     --outfilePrefix <text>  [default: auto]
  711         Prefix for generating output files under output directory.
  712     --overwrite
  713         Overwrite existing files.
  714     --resultFileParams <Name,Value,..>  [default: auto]
  715         A comma delimited list of parameter name and value pairs for writing
  716         calculated RBFEs values to a results file.
  717         
  718         The supported parameter names along with their default values are
  719         are shown below:
  720             
  721             precision, 4  [ Possible values: > 0 ]
  722             delimiter, comma  [ Possible values: comma or tab ]
  723             
  724     -r, --rbfeParams <Name,Value,...>  [default: auto]
  725         A comma delimited list of parameter name and value pairs for RBFE separated
  726         topology protocol settings employed during the calculation of RBFEs.
  727         
  728         The default values are automatically updated to match settings provided by
  729         OpenFE module SepTopProtocol.
  730         
  731         You must specify valid OpenFE values for these parameters. An extensive
  732         validation is not performed.
  733         
  734         The supported parameter names along with their default values are
  735         are shown below:
  736             
  737             protocolRepeats, 3
  738             
  739             Complex equil output settings:
  740             
  741             complexEquilOutputCheckpointInterval, 1  [ Units: nanosecond ]
  742             complexEquilOutputCheckpointStorageFilename, checkpoint.chk
  743             complexEquilOutputEquilNPTStructure, equil_npt.pdb
  744             complexEquilOutputEquilNVTstructure, None
  745             complexEquilOutputForcefieldCache, db.json
  746             complexEquilOutputLogOutput, equil_simulation.log
  747             complexEquilOutputMinimizedStructure, minimized.pdb
  748             complexEquilOutputIndices, all  [ Possible value: Any valid
  749                 selection. ]
  750             complexEquilOutputPreminimizedStructure, system.pdb
  751             complexEquilOutputProductionTrajectoryFilename, production_equil.xtc
  752             complexEquilOutputTrajectoryWriteInterval,  20.0  [ Units:
  753                 picosecond ]
  754             
  755             Complex equil simulation settings:
  756             
  757             complexEquilSimulationEquilibrationLength, 0.1 [ Units: nanosecond ]
  758             complexEquilSimulationEquilibrationLengthNVT, 0.1   [ Units:
  759                 nanosecond ]
  760             complexEquilSimulationMinimizationSteps, 5000
  761             complexEquilSimulationProductionLength, 2.0  [ Units: nanosecond ]
  762             
  763             Complex lambda settings:
  764             
  765             complexLambdaElecA, 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.25 0.5 0.75
  766                 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0  [ Possible values: A space
  767                 delimited list of values between 0.0 and 1.0 ]
  768             complexLambdaElecB, 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0.75 0.5 0.25
  769                 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 [ Possible values: A space
  770                 delimited list of values between 0.0 and 1.0 ]
  771             complexLambdaRestraintsA, 0.0 0.05 0.1 0.3 0.5 0.75 1.0 1.0 1.0
  772                 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 [ Possible values: A
  773                 space delimited list of values between 0.0 and 1.0 ]
  774             complexLambdaRestraintsB, 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
  775                 1.0 1.0 1.0 0.75 0.5 0.3 0.1 0.05 0.0 [ Possible values: A space
  776                 delimited list of values between 0.0 and 1.0 ]
  777             complexLambdaVdwA, 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
  778                 0.143 0.286 0.429 0.572 0.715 0.857 1.0 [ Possible values: A
  779                 delimited list of values between 0.0 and 1.0 ]
  780             complexLambdaVdwB, 1.0 0.857 0.715 0.572 0.429 0.286 0.143 0.0
  781                 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 [ Possible values: A
  782                 delimited list of values between 0.0 and 1.0 ]
  783             
  784             Complex output settings:
  785             
  786             complexOutputCheckpointInterval, 1.0  [ Units: nanosecond ]
  787             complexOutputCheckpointStorageFilename, complex_checkpoint.nc
  788             complexOutputForcefieldCache, db.json
  789             complexOutputFilename, complex.nc
  790             complexOutputIndices, not water  [ Possible value: Any valid
  791                 selection. ]
  792             complexOutputStructure, alchemical_system.pdb
  793             complexOutputPositionsWriteFrequency, 100.0  [ Units: picosecond ]
  794             complexOutputVelocitiesWriteFrequency, None  [ Possible
  795                 values: > 0; Units: picosecond ]
  796             
  797             Complex restraint settings:
  798             
  799             complexRestraintKPhiA, 334.72  [ Units: kilojoule_per_mole/radian**2
  800                 The default value is equivalent to 80 kcal/mol/radian**2 ]
  801             complexRestraintKPhiB, 334.72  [ Units: kilojoule_per_mole/radian**2
  802                 The default value is equivalent to 80 kcal/mol/radian**2 ]
  803             complexRestraintKPhiC, 334.72  [ Units: kilojoule_per_mole/radian**2
  804                 The default value is equivalent to 80 kcal/mol/radian**2 ]
  805             complexRestraintKR, 4184.0  [ Units: kilojoule_per_mole/nanometer**2
  806                  The default value is equivalent to 10 kcal/mol/angstrom**2
  807             complexRestraintKThetaA, 334.72  [ Units:kilojoule_per_mole/radian**2
  808                 The default value is equivalent to 80 kcal/mol/radian**2 ]
  809             complexRestraintKThetaB, 334.72  [ Units:kilojoule_per_mole/radian**2
  810                 The default value is equivalent to 80 kcal/mol/radian**2 ]
  811             complexRestraintAnchorFindingStrategy, bonded  [ Possible values:
  812                 multi-residue or bonded ] 
  813             complexRestraintDsspFilter, yes   [ Possible values: yes or no ]
  814             complexRestraintHostMaxDistance, 1.5  [ Units: nanometer ]
  815             complexRestraintHostMinDistance, 0.5  [ Units: nanometer ]
  816             complexRestraintHostSelection, backbone   [ Possible value: Any valid
  817                 selection. ]
  818             complexRestraintRmsfCutoff, 0.1  [ Units: nanometer ]
  819             
  820             Complex simulation settings:
  821             
  822             complexSimulationEarlyTerminationTargetError, 0.0  [ Units:
  823                 kilocalorie_per_mole ]
  824             complexSimulationEquilibrationLength,  1.0  [ Units: nanosecond ]
  825             complexSimulationMinimizationSteps, 5000
  826             complexSimulationNReplicas, 19
  827             complexSimulationProductionLength, 10.0  [ Units: nanosecond ]
  828             complexSimulationRealTimeAnalysisInterval, 250.0  [ Units:
  829                 picosecond ]
  830             complexSimulationRealTimeAnalysisMinimumTime, 500.0  [ Units:
  831                 picosecond ]
  832             complexSimulationSamplerMethod, repex  [ Possible values: repex,
  833                 sams, or independent ]
  834             complexSimulationSamsFlatnessCriteria, logZ-flatness  [ Possible
  835                 values: logZ-flatness, minimum-visits or histogram-flatness ]
  836             complexSimulationSamsGamma0, 1.0
  837             complexSimulationTimePerIteration, 2.5   [ Units: picosecond ]
  838             
  839             Complex solvation settings:
  840             
  841             complexSolvationBoxShape, dodecahedron  [  Possible values: cube,
  842                 dodecahedron, or octahedron ]
  843             complexSolvationBoxSize, None  [ Possible value: A triplet of space
  844                 X Y Z values; Units: nanometer ]
  845             complexSolvationSolventModel, tip3p  [ Possible values: tip3p, spce,
  846                 tip4pew, or tip5p ]
  847             complexSolvationSolventPadding, 1.0  [ Units: nanometer ]
  848             
  849             Engine settings:
  850             
  851             engineComputePlatform, CPU  [ Possible values: CPU, CUDA,
  852                 OpenCL, or Reference ]
  853             engineGpuDeviceIndex, None [ Possible values: 0, 0 1, etc. ]
  854              
  855             Forcefield settings:
  856             
  857             forcefieldConstraints, HBonds  [ Possible values: HBonds,
  858                 AllBonds, or HAngles ]
  859             forcefields, amber/ff14SB.xml amber/tip3p_standard.xml
  860                 amber/tip3p_HFE_multivalent.xml amber/phosaa10.xml
  861                 [ Possible values: A space delimited list of valid names. ]
  862             forcefieldHydrogenMass, 3.0  [ Units: amu ]
  863             forcefieldNonbondedCutoff, 0.9   [ Units: nanometer ]
  864             forcefieldNonbondedMethod, PME  [ Possible values: PME or
  865                 NoCutoff ]
  866             forcefieldRigidWater, yes  [ Possible values: yes or no ]
  867             forcefieldSmallMoleculeForcefield, openff-2.1.1  [ Possible
  868                 value: A valid forcefield name. ]
  869             
  870             Integrator settings:
  871             
  872             integratorBarostatFrequency, 25.0 * timestep  [ The specified value
  873                 is a multiple of integratorTimestep. ]
  874             integratorConstraintTolerance, 1e-06
  875             integratorLangevinCollisionRate, 1.0  [ Units: 1 / picosecond ]
  876             integratorNRestartAttempts, 20
  877             integratorReassignVelocities, no  [ Possible values: yes or no ]
  878             integratorRemoveCom, no  [ Possible values: yes or no ]
  879             integratorTimestep, 4.0 [ Units: femtosecond ] 
  880             
  881             Partial charge settings:
  882             
  883             partialChargeNaglModel, None  [ Default: Production AM1BCC model for
  884                 NAGL; Possible value: Any valid name. ]
  885             partialChargeNumberOfConformers, None  [ Possible value: > 0 ]
  886             partialChargeOffToolkitBackend, AmberTools  [ Possible values:
  887                 AmberTools or RDKit ]
  888             partialChargeMethod, AM1BCC  [ Possble values: AM1BCC, Espaloma,
  889                 or NAGL ]
  890             
  891             Solvent equil output settings:
  892             
  893             solventEquilOutputCheckpointInterval, 1.0  [ Units: nanosecond ]
  894             solventEquilOutputCheckpointStorageFilename, checkpoint.chk
  895             solventEquilOutputEquilNPTStructure, equil_npt.pdb
  896             solventEquilOutputEquilNVTstructure, None
  897             solventEquilOutputForcefieldCache, db.json
  898             solventEquilOutputLogOutput, equil_simulation.log
  899             solventEquilOutputMinimizedStructure, minimized.pdb
  900             solventEquilOutputIndices, all  [  Possible value: Any valid
  901                 selection. ]
  902             solventEquilOutputPreminimizedStructure, system.pdb
  903             solventEquilOutputProductionTrajectoryFilename, equil_npt.xtc
  904             solventEquilOutputTrajectoryWriteInterval, 20.0  [ Units:
  905                 picosecond ]
  906             
  907             Solvent_equil_simulation_settings:
  908             
  909             solventEquilSimulationEquilibrationLength, 0.1 [ Units: nanosecond ]
  910             solventEquilSimulationEquilibrationLengthNVT, 0.1  [ Units:
  911                 nanosecond ]
  912             solventEquilSimulationMinimizationSteps, 5000
  913             solventEquilSimulationProductionLength, 2.0  [ Units: nanosecond ]
  914             
  915             Solvent lambda settings:
  916             
  917             solventLambdaElecA, 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.125
  918                 0.25 0.375 0.5 0.625 0.75 0.875 1.0 1.0 1.0 1.0 1.0 1.0 1.0
  919                 1.0 1.0 1.0 [ Possible values: A space delimited list of values
  920                 between 0.0 and 1.0 ]
  921             solventLambdaElecB, 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0.875
  922                 0.75 0.625 0.5 0.375 0.25 0.125 0.0 0.0 0.0 0.0 0.0 0.0 0.0
  923                 0.0 0.0 0.0 [ Possible values: A space delimited list of values
  924                 between 0.0 and 1.0 ]
  925             solventLambdaRestraintsA, 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
  926                 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
  927                 0.0 [ Possible values: A space delimited list of values between
  928                 0.0 and 1.0 ]
  929             solventLambdaRestraintsB, 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
  930                 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
  931                 0.0 [ Possible values: A space delimited list of values between
  932                 0.0 and 1.0 ]
  933             solventLambdaVdwA, 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
  934                 0.0 0.0 0.0 0.0 0.0 0.0 0.15 0.23 0.3 0.4 0.52 0.64 0.76 0.88
  935                 1.0 [ Possible values: A space delimited list of values between
  936                 0.0 and 1.0 ]
  937             solventLambdaVdwB, 1.0 0.85 0.77 0.7 0.6 0.48 0.36 0.24 0.12 0.0
  938                 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
  939                 0.0 [ Possible values: A space delimited list of values between
  940                 0.0 and 1.0 ]
  941             
  942             Solvent output settings:
  943             
  944             solventOutputCheckpointInterval, 1.0  [ Units: nanosecond ]
  945             solventOutputCheckpointStorageFilename, solvent_checkpoint.nc
  946             solventOutputForcefieldCache, db.json
  947             solventOutputFilename, solvent.nc
  948             solventOutputIndices, not water  [ Possible value: Any valid
  949                 selection. ]
  950             solventOutputStructure, alchemical_system.pdb
  951             solventOutputPositionsWriteFrequency, 100.0  [ Units: picosecond ]
  952             solventOutputVelocitiesWriteFrequency, None  [ Possible
  953                 values: > 0; Units: picosecond ]
  954             
  955             Solvent restraint settings:
  956             
  957             solventRestraintCentralAtomsOnly, No  [ Possible values: yes or no ]
  958             solventRestraintSpringConstant, 1000.0 [ Units: kilojoule_per_mole /
  959                 nanometer ** 2. The default value is equivalent to 2.40
  960                 kilocalorie_per_mole / angstromg ** 2 ]
  961             
  962             Solvent simulation settings:
  963             
  964             solventSimulationEarlyTerminationTargetError, 0.0  [ Units:
  965                 kilocalorie_per_mole ]
  966             solventSimulationEquilibrationLength, 1.0  [ Units: nanosecond ]
  967             solventSimulationMinimizationSteps, 5000
  968             solventSimulationNReplicas, 27
  969             solventSimulationProductionLength, 10.0  [ Units: nanosecond ]
  970             solventSimulationRealTimeAnalysisInterval, 250.0  [ Unit: picosecond ]
  971             solventSimulationRealTimeAnalysisMinimumTime, 500.0  [ Units:
  972                 picosecond ]
  973             solventSimulationSamplerMethod, repex  [ Possible values: repex,
  974                 sams, or independent ]
  975             solventSimulationSamsFlatnessCriteria, logZ-flatness  [ Possible
  976                 values: logZ-flatness, minimum-visits or histogram-flatness ]
  977             solventSimulationSamsGamma0, 1.0
  978             solventSimulationTimePerIteration, 2.5  [ Units: picosecond ]
  979             
  980             Solvent solvation settings:
  981             
  982             solventSolvationBoxShape, dodecahedron  [  Possible values: cube,
  983                 dodecahedron, or octahedron ]
  984             solventSolvationBoxSize, None  [ Possible value: A triplet of space
  985                 X Y Z values; Units: nanometer ]
  986             solventSolvationSolventModel, tip3p  [ Possible values: tip3p, spce,
  987                 tip4pew, or tip5p ]
  988             solventSolvationSolventPadding, 1.5  [ Units: nanometer ]
  989             
  990             Thermo settings:
  991             
  992             thermoPh, None  [ Possible values: > 0 ]
  993             thermoPressure, 1.0  [ Units: bar ]
  994             thermoRedoxPotential, None  [ Possible values: A valid float.
  995                 Units: millivolts (mV) ]
  996             thermoTemperature, 298.15  [ Units: kelvin ]
  997             
  998         A brief description of parameters, taken from OpenFE documentation, is
  999         provided below:
 1000             
 1001             protocolRepeats: Number of completely independent repeats of the
 1002                 entire sampling process.
 1003             
 1004             Complex settings:
 1005             
 1006             Complex parameters for the system, including the solvent model and
 1007             the solvent padding.
 1008             
 1009             Complex equil output settings:
 1010             
 1011             Parameters controlling simulation output during equilibration
 1012             phase of complex transformation.
 1013             
 1014             complexEquilOutputCheckpointInterval: Frequency to write the
 1015                 checkpoint file.
 1016             complexEquilOutputCheckpointStorageFilename: Checkpoint filename.
 1017             complexEquilOutputEquilNPTStructure: NPT structure filename.
 1018             complexEquilOutputEquilNVTstructure: NVT strucure filename.
 1019             complexEquilOutputForcefieldCache:  Filename for caching small
 1020                 molecule residue templates.
 1021             complexEquilOutputLogOutput: Simulation log filename.
 1022             complexEquilOutputMinimizedStructure: Minimized structure filename.
 1023             complexEquilOutputIndices: Selection string for selecting
 1024                 coordinates to write.
 1025             complexEquilOutputPremnimizedStructure: Initial structure filename.
 1026             complexEquilOutputProductionTrajectoryFilename: Trajectory filename.
 1027             complexEquilOutputTrajectoryWriteInterval: Frequency for writing
 1028                 velocities to trajectory file.
 1029             
 1030             Complex equil simulation settings:
 1031             
 1032             Parameters controlling simulation during equilibration phase of
 1033             complex transformation.
 1034             
 1035             complexEquilSimulationEquilibrationLength:  Length of the NPT
 1036                 equilibration phase.
 1037             complexEquilSimulationEquilibrationLengthNVT: Length of the NVT
 1038                 equilibration phase.
 1039             complexEquilSimulationMinimizationSteps: Maximum number of
 1040                 minimization steps to perform.
 1041             complexEquilSimulationProductionLength:  Length of the NPT
 1042                 production phase.
 1043             
 1044             Complex lambda settings:
 1045             
 1046             Lambda protocol parameters for complex transformation.
 1047             
 1048             complexLambdaElecA: List of lambda values for electrostatics. The
 1049                 values of 0 and 1 imply state A and state B respectively.
 1050             complexLambdaElecB: List of lambda values for electrostatics. The
 1051                 values of 0 and 1 imply state A and state B respectively.
 1052             complexLambdaRestraintsA: List of lambda values for restraints. The
 1053                 values of 0 and 1 imply state A and state B respectively.
 1054             complexLambdaRestraintsB: List of lambda values for restraints. The
 1055                 values of 0 and 1 imply state A and state B respectively.
 1056             complexLambdaVdwA: List of lamda values for van der Waals. The
 1057                 values of of 0 and 1 imply state A and state B respectively.
 1058             complexLambdaVdwB: List of lamda values for van der Waals. The
 1059                 values of of 0 and 1 imply state A and state B respectively.
 1060             
 1061             Complex output settings:
 1062             
 1063             Parameters controlling simulation output during final phase of
 1064             complex transformation.
 1065             
 1066             complexOutputCheckpointInterval:  Frequency to write the checkpoint
 1067                 file.
 1068             complexOutputCheckpointStorageFilename: Checkpoint filename.
 1069             complexOutputForcefieldCache: Filename for caching small molecule
 1070                 residue templates.
 1071             complexOutputFilename: Trajectory filename.
 1072             complexOutputIndices: Selection string for selecting coordinates to
 1073                 write.
 1074             complexOutputStructure: Topology structure filename.
 1075             complexOutputPositionsWriteFrequency: Frequency for writing
 1076                 positions to trajectory file.
 1077             complexOutputVelocitiesWriteFrequency: Frequency for writing
 1078                 velocities to trajectory file.
 1079             
 1080             Complex restraint settings:
 1081             
 1082             Parameters to configure Boresch-style restraint between two groups
 1083             of atoms named host  (Hx) and guest (Gx).
 1084             
 1085             complexRestraintKPhiA: Equilibrium force constant for the dihedral
 1086                 formed by H2-H1-H0-G0.
 1087             complexRestraintKPhiB: Equilibrium force constant for the dihedral
 1088                 formed by H1-H0-G0-G1.
 1089             complexRestraintKPhiC: Equilibrium force constant for the dihedral
 1090                 formed by H0-G0-G1-G2.
 1091             complexRestraintKR: Bond spring constant between H0 and G0.
 1092             restraintKThetaA: Spring constant for the angle formed by H1-H0-G0.
 1093             complexRestraintKThetaA: Spring constant for the angle formed by
 1094                 H1-H0-G0.
 1095             complexRestraintKThetaB:  Spring constant for the angle formed by
 1096                 H0-G0-G1.
 1097             complexRestraintAnchorFindingStrategy: Boresch atom picking strategy
 1098                 to use. bonded: pick host atoms that are bonded to each other.
 1099                 multi-residue: pick host atoms which can span multiple residues.
 1100             complexRestraintDsspFilter: Apply DSSP filter to the host atoms.
 1101             complexRestraintHostMaxDistance: Maximum distance between any
 1102                 host atom and the guest G0 atom.
 1103             complexRestraintHostMinDistance: Minimum distance between any
 1104                 host atom and the guest G0 atom
 1105             complexRestraintHostSelection: A valid selection string to
 1106                 sub-select the host atoms which will be involved in the
 1107                 restraint.
 1108             complexRestraintRmsfCutoff: Cutoff value for filtering atoms by their
 1109                 root mean square fluctuation. Atoms with values above this
 1110                 cutoff are ignored.
 1111             
 1112             Complex simulation settings:
 1113             
 1114             Parameters controlling simulation during final phase of complex
 1115             transformation.
 1116             
 1117             complexSimulationEarlyTerminationTargetError: Target error for the
 1118                 real time analysis measured in kcal/mol. Once the MBAR error of
 1119                 the free energy is at or below this value, the simulation will
 1120                 be considered complete. The suggested value of 0.12 has shown to
 1121                 be effective in both hydration and binding free energy
 1122                 benchmarks.
 1123             complexSimulationEquilibrationLength: Length of the equilibration
 1124                 phase. The specified value must be divisible by
 1125                 'integratorTimestep'.
 1126             complexSimulationMinimizationSteps: Maximum number of minimization
 1127                 steps to perform.
 1128             complexSimulationNReplicas: Number of replicas to use.
 1129             complexSimulationProductionLength: Length of the production phase.
 1130                 The specified value must be divisible by 'integratorTimestep'.
 1131             complexSimulationRealTimeAnalysisMinimumTime: Time interval for
 1132                 performing analysis of the free energies. At each interval, real
 1133                 time analysis data will be written to a yaml file named
 1134                 <outputFileName>_real_time_analysis.yaml. The current error
 1135                 in the estimate will also be assessed and the simulation will
 1136                 be terminated when it drops below
 1137                 'complexSimulationEarlyTerminationTargetError'.
 1138             complexSimulationSamplerMethod: Alchemical sampling method to use:
 1139                 REPEX (Hamiltonian REPlica EXchange), SAMS (Self-Adjusted
 1140                 Mixture Sampling), or Independent (Independently sampled lambda
 1141                 windows).
 1142             complexSimulationSamsFlatnessCriteria:Method for assessing when to
 1143                 switch to asymptomatically optimal scheme for SAMS.
 1144             complexSimulationsamsGamma0: Initial weight adaptation rate for
 1145                 SAMS.
 1146             complexSimulationTimePerIteration: Simulation time between each
 1147                MCMC move attempt 
 1148             
 1149             Complex solvation settings:
 1150             
 1151             Solvation parameters for the system, including the solvent model and
 1152             the solvent padding.
 1153             
 1154             complexSolvationBoxShape: Shape of the periodic solvent box.
 1155             complexSolvationBoxSize:  Lengths of the unit cell for a solvent box.
 1156             complexSolvationSolventModel: Forcefield water model to use during
 1157                 solvation and defining the model properties.
 1158             complexSolvationSolventPadding: Minimum distance from any solute
 1159                 bounding sphere to the edge of the box.
 1160             
 1161             Engine settings:
 1162             
 1163             Parameters configuring the compute platform used by the OpenMM to
 1164             perform the simulation.
 1165             
 1166             engineComputePlatform: Platform to use for running OpenMM MD
 1167                 calculations.
 1168             engineGpuDeviceIndex: Space delimited list of device indices
 1169                 to use for running OpenMM MD calculations.
 1170             
 1171             Forcefield settings:
 1172             
 1173             forcefieldConstraints:Constraints  to use.
 1174             forcefields: List of valid forcefield paths for all components
 1175                 except small molecules.
 1176             forcefieldHydrogenMass: Mass to be repartitioned to hydrogens
 1177                 from neighboring heavy atoms.
 1178             forcefieldNonbondedCutoff: Cutoff for short range nonbonded
 1179                 interactions.
 1180             forcefieldNonbondedMethod: Method for treating nonbonded
 1181                 interactions.
 1182             forcefieldRigidWater: Use a rigid water model.
 1183             forcefieldSmallMoleculeForcefield: A valid forcefield name to use
 1184                 for small molecules.
 1185             
 1186             Integrator settings:
 1187             
 1188             Parameters controlling the LangevinSplittingDynamicsMove integrator
 1189             used for simulation.
 1190             
 1191             integratorBarostatFrequency: Frequency at which volume scaling
 1192                 changes should be attempted.
 1193             integratorConstraintTolerance: Tolerance for constraint solver.
 1194             integratorLangevinCollisionRate: Collision frequency.
 1195             integratorNRestartAttempts: Number of attempts to restart from
 1196                 Context in case there are NaNs in the energies after
 1197                 integration.
 1198             integratorReassignVelocities: Reassign velocities  from the
 1199                 Maxwell-Boltzmann distribution at the beginning of each
 1200                 Monte Carlo move.
 1201             integratorRemoveCom: Remove the center of mass motion.
 1202             integratorTimestep: Size of the simulation timestep.
 1203             
 1204             Partial charge settings:
 1205             
 1206             Parameters for automatically assigning missing partial charges to
 1207             small molecules, including the partial charge method.
 1208             
 1209             partialChargeNaglModel: Model to use for partial charge assignment.
 1210                 A value of None implies the use of the latest available
 1211                 production AM1BCC model.
 1212             partialChargeNumberOfConformers: Number of conformers to generate
 1213                 as part of the partial charge assignment. A value of None
 1214                 implies the use of the existing conformer.
 1215             partialChargeOffToolkitBackend: OpenFF toolkit registry backend to
 1216                 use for calculating partial charges.
 1217             partialChargeMethod: Method to use for calculating partial charges.
 1218             
 1219             Solvent equil output settings:
 1220             Solvent equil simulation settings:
 1221             Solvent lambda settings:
 1222             Solvent output settings:
 1223             Solvent restraint settings:
 1224             Solvent simulation settings:
 1225             Solvent solvation settings:
 1226             
 1227             The solvent settings are similar to the complex settings already
 1228             described under various sections for complex. The prefix 'solvent'
 1229             is used for the names of the pramaters instead of the prefix
 1230             'complex.'
 1231             
 1232             Thermo settings:
 1233             
 1234             Thermodynamic parameters, including the temperature and the pressure
 1235             of the system.
 1236             
 1237             thermoPh: Simulation pH
 1238             thermoPressure: Simulation pressure.
 1239             thermoRedoxPotential:Simulation redox potential.
 1240             thermoTemperature: Simulation temperature. 
 1241             
 1242     -s, --smallMolFile <SmallMolFile>
 1243         Input file containing small molecules.
 1244     --smallMolFileParams <Name,Value,...>  [default: auto]
 1245         A comma delimited list of parameter name and value pairs for reading
 1246         molecules from files. The supported parameter names for different file
 1247         formats, along with their default values, are shown below:
 1248             
 1249             SD: removeHydrogens,no,sanitize,yes,strictParsing,yes
 1250             
 1251     --solventParams <Name,Value,...>  [default: auto]
 1252         A comma delimited list of parameter name and value pairs for solvent
 1253         component. You must specify valid OpenFE values. No extensive validation
 1254         is performed. These parameters are used in conjunction with solvation*
 1255         parameters available through '--rbfeParams' to perform solvation.
 1256         
 1257         The supported parameter names along with their default values are
 1258         are shown below:
 1259             
 1260             positiveIon, Na+ [ Possible value: Li+, Na+, K+, Rb+, or Cs+ ]
 1261             negativeIon, Cl- [ Possible values: Cl-, Br-, F-, or I- ]
 1262             neutralize, yes  [ Possible values: yes or no ]
 1263             ionConcentration, 0.15  [ Units: molar ]
 1264             
 1265         A brief description of parameters is provided below:
 1266             
 1267             positiveIon, negativeion: Pair of ions used to neutralize and bring
 1268                 the solvent to required ionic concentration.
 1269             neutralize: Neutralize the net charge on the chemical state by the
 1270                 ions in the solvent component.
 1271             ionConcentration: Ionic concentration.
 1272             
 1273     -w, --workingdir <dir>
 1274         Location of working directory which defaults to the current directory.
 1275 
 1276 Examples:
 1277     The sample protein and ligand files for tyrosine kinase 2 (Tyk2) are
 1278     distributed with MayaChemTools and are available in data directory. These
 1279     files have been taken from OpenFE distribution for example notebooks. The
 1280     AM1BCC partial charges have been calculated for the ligands in SD file to
 1281     facilitate calculations. You may review OpenFE tutorial notebooks for the
 1282     expected results.
 1283 
 1284     To calculate RBFE for a pair molecules corresponding to the fist and second
 1285     molecules in a SD file, performing 3 independent repeats of the entire MD
 1286     sampling process to calculate RBFE for a pair of molecules, each complex
 1287     and solvent MD repeat consisting of equilibration phase (Complex:
 1288     Minimization - 5,000; NVT - 0.1 ns; NPT - 0.1; NPT prod - 2.0 ns; Solvent:
 1289     Minimization  - 5,000; NVT - 0.1; NPT - 0.1 ns; NPT prod - 2.0 ns) and
 1290     production phase ( Complex: Minimization - 5,000; NPT equil - 1.0 ns; NPT prod:
 1291     10.0 ns; Solvent: Minimization - 5,000; NPT equil - 1.0 ns; NPT prod - 10 ns)
 1292     using a step size of of 4 fs, writing out appropriate trajectory and PDB files for
 1293     each MD repeat in Results subdirectory under output directory, type:
 1294 
 1295         % OpenFECalculateRelativeBindingFreeEnergySepTop.py -i SampleTyk2.pdb
 1296           -s SampleTyk2Ligands.sdf -o SampleTyk2LigandsRBFESepTop
 1297 
 1298     To run the first example for calculating RBFE for a specific pair molecules
 1299     using CUDA platform on your machine to perform MD simulations and generate
 1300     various output files, type:
 1301 
 1302         % OpenFECalculateRelativeBindingFreeEnergySepTop.py -i SampleTyk2.pdb
 1303           -s SampleTyk2Ligands.sdf -o SampleTyk2LigandsRBFESepTop
 1304           --moleculePairs "lig_ejm_31, lig_ejm_47"
 1305           --rbfeParams "engineComputePlatform,CUDA"
 1306 
 1307     To run the second example to see all warning messages produced by OpenFE
 1308     modules and write various output files, type;
 1309 
 1310         % OpenFECalculateRelativeBindingFreeEnergySepTop.py -i SampleTyk2.pdb
 1311           -s SampleTyk2Ligands.sdf -o SampleTyk2LigandsRBFESepTop
 1312           --moleculePairs "lig_ejm_31, lig_ejm_47"
 1313           --rbfeParams "engineComputePlatform,CUDA"
 1314           --loggingLevel Warning
 1315 
 1316     To run the first example for calculating RBFE for a specific pair molecules
 1317     using CUDA platform on your machine to perform MD simulations, automatically
 1318     calculate missing partial charges for molecules, and generate various output
 1319     files, type:
 1320 
 1321         % OpenFECalculateRelativeBindingFreeEnergySepTop.py -i SampleTyk2.pdb
 1322           -s SampleTyk2Ligands.sdf -o SampleTyk2LigandsRBFESepTop
 1323           --moleculePairs "lig_ejm_31, lig_ejm_47"
 1324           --rbfeParams "engineComputePlatform,CUDA"
 1325           --missingChargeMode Calculate
 1326 
 1327     To run the second example by specifying explict values for various parametres
 1328     and generate various output files, type:
 1329 
 1330         % OpenFECalculateRelativeBindingFreeEnergySepTop.py -i SampleTyk2.pdb
 1331           -s SampleTyk2Ligands.sdf -o SampleTyk2LigandsRBFESepTop
 1332           --moleculePairs "lig_ejm_31, lig_ejm_47"
 1333           --loggingLevel Warning
 1334           --executeDAGParams "keepShared, yes, nRetries, 2"
 1335           --missingChargeMode Stop
 1336           --rbfeParams "engineComputePlatform,CUDA,
 1337           protocolRepeats,3,
 1338           engineComputePlatform,CUDA,
 1339           forcefieldConstraints, HBonds, forcefieldHydrogenMass, 3.0,
 1340           forcefieldNonbondedMethod, PME, integratorTimestep, 4.0,
 1341           complexEquilSimulationMinimizationSteps, 5000,
 1342           complexEquilSimulationEquilibrationLength, 0.1,
 1343           complexEquilSimulationEquilibrationLengthNVT, 0.1,
 1344           complexEquilSimulationProductionLength, 2.0,
 1345           complexSimulationMinimizationSteps, 5000,
 1346           complexSimulationEquilibrationLength,  1.0,
 1347           complexSimulationProductionLength, 10.0,
 1348           solventEquilSimulationMinimizationSteps, 5000,
 1349           solventEquilSimulationEquilibrationLength, 0.1,
 1350           solventEquilSimulationEquilibrationLengthNVT, 0.1,
 1351           solventEquilSimulationProductionLength, 2.0,
 1352           solventSimulationMinimizationSteps, 5000,
 1353           solventSimulationEquilibrationLength, 1.0,
 1354           solventSimulationProductionLength, 10.0"
 1355           --solventParams "positiveIon, Na+, negativeIon, Cl-"
 1356 
 1357 Author:
 1358     Manish Sud(msud@san.rr.com)
 1359 
 1360 See also:
 1361    OpenFECalculateAbsoluteBindingFreeEnergy.py,
 1362    OpenFECalculateAbsoluteHydrationFreeEnergy.py, OpenFECalculatePartialCharges.py,
 1363    OpenFECalculateRelativeHydrationFreeEnergy.py, OpenFEGenerateLigandNetwork.py
 1364 
 1365 Copyright:
 1366     Copyright (C) 2026 Manish Sud. All rights reserved.
 1367 
 1368     The functionality available in this script is implemented using OpenFE, an
 1369     open source molecuar for alchemical free energy calculations.
 1370 
 1371     This file is part of MayaChemTools.
 1372 
 1373     MayaChemTools is free software; you can redistribute it and/or modify it under
 1374     the terms of the GNU Lesser General Public License as published by the Free
 1375     Software Foundation; either version 3 of the License, or (at your option) any
 1376     later version.
 1377 
 1378 """
 1379 
 1380 if __name__ == "__main__":
 1381     main()