MayaChemTools

    1 #!/bin/env python
    2 #
    3 # File: OpenFECalculateRelativeBindingFreeEnergy.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 numpy as np
   38 import pandas as pd
   39 
   40 # OpenFE imports...
   41 try:
   42     import openfe
   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."""
  107 
  108     # Process input files...
  109     MacroMol, Mols = ProcessInputFiles()
  110 
  111     # Validate molecule names...
  112     ValidateMoleculeNames(Mols)
  113 
  114     # Check for miising partial charges...
  115     CheckMissingPartialCharges(Mols)
  116 
  117     # Setup atom mapping...
  118     MolAToMolBMappings = GenerateAtomMappings(Mols)
  119 
  120     # Initialize RBFE protocols...
  121     RBFEProtocol, RBFEProtocolChargeCorrection = InitializeRelativeHybridTopologyProtocol()
  122 
  123     # Initialize solvent...
  124     Solvent = InitializeSolventComponent()
  125 
  126     # Setup transformations...
  127     MolAToMolBTransformations = SetupTransformations(
  128         MolAToMolBMappings, MacroMol, Solvent, RBFEProtocol, RBFEProtocolChargeCorrection
  129     )
  130 
  131     # Setup protocol DAGs...
  132     MolAToMolBProtocolDAGs = SetupProtocolDAGs(MolAToMolBTransformations)
  133 
  134     # Execute protocol DAGs and gather results...
  135     MolAToMolBProtocolResults = ExecuteProtocolDAGsAndGatherResults(MolAToMolBTransformations, MolAToMolBProtocolDAGs)
  136 
  137     # Process protocol results...
  138     ProcessProtocolResults(MolAToMolBTransformations, MolAToMolBProtocolResults)
  139 
  140 
  141 def InitializeRelativeHybridTopologyProtocol():
  142     """Initialize relative hybrid toplology protocol."""
  143 
  144     MiscUtil.PrintInfo("\nInitializing relative hybrid topology protocol...")
  145 
  146     RBFESettings = OpenFEUtil.SetupRelativeFreeEnergySettings("-r, --rbfeParams", OptionsInfo["RBFEParams"])
  147     RBFEProtocol = OpenFEUtil.InitializeRelativeFreeEngeryHybridTopologyProtocol(RBFESettings)
  148 
  149     RBFESettingsChargeCorrection = OpenFEUtil.SetupRelativeFreeEnergySettings(
  150         "-r, --rbfeParams", OptionsInfo["RBFEParams"]
  151     )
  152     OpenFEUtil.UpdateRelativeFreeEnergySettingsForChargeCorrection(
  153         "--rbfeChargeCorrectionParams", OptionsInfo["RBFEChargeCorrectionParams"], RBFESettingsChargeCorrection
  154     )
  155     RBFEProtocolChargeCorrection = OpenFEUtil.InitializeRelativeFreeEngeryHybridTopologyProtocol(
  156         RBFESettingsChargeCorrection
  157     )
  158 
  159     return (RBFEProtocol, RBFEProtocolChargeCorrection)
  160 
  161 
  162 def InitializeSolventComponent():
  163     """Initialize solvent component."""
  164 
  165     SolventParams = OptionsInfo["SolventParams"]
  166     MiscUtil.PrintInfo(
  167         "\nInitializing solvent component (PositiveIon: %s; NegativeIon: %s; Neutralize: %s; IonConcentration: %s)..."
  168         % (
  169             SolventParams["PositiveIon"],
  170             SolventParams["NegativeIon"],
  171             SolventParams["Neutralize"],
  172             SolventParams["IonConcentration"],
  173         )
  174     )
  175 
  176     Solvent = OpenFEUtil.InitializeSolventComponent(SolventParams)
  177 
  178     return Solvent
  179 
  180 
  181 def SetupTransformations(MolAToMolBMappings, MacroMol, Solvent, RBFEProtocol, RBFEProtocolChargeCorrection):
  182     """Set up a transformation pair for each mapping."""
  183 
  184     MiscUtil.PrintInfo("\nSetting up alchemical transformations (Count: %s)..." % (len(MolAToMolBMappings) * 2))
  185 
  186     MolAToMolBTransformations = []
  187 
  188     for MolAToMolBMapping in MolAToMolBMappings:
  189         MolA = MolAToMolBMapping.componentA
  190         MolB = MolAToMolBMapping.componentB
  191 
  192         # Setup chemical systems...
  193         MolAComplexSolventSystem = OpenFEUtil.InitializeChemicalSystem(
  194             SmallMol=MolA, MacroMol=MacroMol, Solvent=Solvent, Name="%s_Complex_Solvent" % MolA.name
  195         )
  196         MolASolventSystem = OpenFEUtil.InitializeChemicalSystem(
  197             SmallMol=MolA, MacroMol=None, Solvent=Solvent, Name="%s_Solvent" % MolA.name
  198         )
  199 
  200         MolBComplexSolventSystem = OpenFEUtil.InitializeChemicalSystem(
  201             SmallMol=MolB, MacroMol=MacroMol, Solvent=Solvent, Name="%s_Complex_Solvent" % MolB.name
  202         )
  203         MolBSolventSystem = OpenFEUtil.InitializeChemicalSystem(
  204             SmallMol=MolB, MacroMol=None, Solvent=Solvent, Name="%s_Solvent" % MolB.name
  205         )
  206 
  207         # Setup transformation protocol...
  208         TransformationProtocol = SetupTransformationProtocol(
  209             MolAToMolBMapping, RBFEProtocol, RBFEProtocolChargeCorrection
  210         )
  211 
  212         # Setup MolAComplexSolvent to MolBComplexSolvent transformation...
  213         TransformationName = "%s_To_%s_Complex_Solvent" % (MolA.name, MolB.name)
  214         MolAToMolBComplexSolventTransformation = OpenFEUtil.InitializeTransformation(
  215             StateA=MolAComplexSolventSystem,
  216             StateB=MolBComplexSolventSystem,
  217             Mapping=MolAToMolBMapping,
  218             Protocol=TransformationProtocol,
  219             Name=TransformationName,
  220             Validate=False,
  221         )
  222         MolAToMolBTransformations.append(MolAToMolBComplexSolventTransformation)
  223 
  224         # Setup MolASolvent to MolBSolvent transformation...
  225         TransformationName = "%s_To_%s_Solvent" % (MolA.name, MolB.name)
  226         MolAToMolBSolventTransformation = OpenFEUtil.InitializeTransformation(
  227             StateA=MolASolventSystem,
  228             StateB=MolBSolventSystem,
  229             Mapping=MolAToMolBMapping,
  230             Protocol=RBFEProtocol,
  231             Name=TransformationName,
  232             Validate=False,
  233         )
  234         MolAToMolBTransformations.append(MolAToMolBSolventTransformation)
  235 
  236     # Write out transformatios...
  237     WriteTransformations(MolAToMolBTransformations)
  238 
  239     return MolAToMolBTransformations
  240 
  241 
  242 def SetupTransformationProtocol(MolAToMolBMapping, RBFEProtocol, RBFEProtocolChargeCorrection):
  243     """Setup transformation protocol."""
  244 
  245     from openfe.utils import ligand_utils
  246 
  247     ChargeDifference = ligand_utils.get_alchemical_charge_difference(MolAToMolBMapping)
  248 
  249     if ChargeDifference != 0:
  250         MolA = MolAToMolBMapping.componentA
  251         MolB = MolAToMolBMapping.componentB
  252         if OptionsInfo["RBFEChargeCorrection"]:
  253             TransformationProtocol = RBFEProtocolChargeCorrection
  254             MiscUtil.PrintInfo("")
  255             MiscUtil.PrintWarning(
  256                 'The transformation between molecules %s and %s involves a charge change of %s. The RBFE setting parameters have been automatically updated for "Yes" value of option "--rbfeChargeCorrection" to employ a more expensive set of parameters specified by option "--rbfeChargeCorrectionParams". '
  257                 % (MolA.name, MolB.name, ChargeDifference)
  258             )
  259         else:
  260             TransformationProtocol = RBFEProtocol
  261             MiscUtil.PrintInfo("")
  262             MiscUtil.PrintWarning(
  263                 'The transformation between molecules %s and %s involves a charge change of %s. The RBFE setting parameters have not been automatically updated for "No" value of option "--rbfeChargeCorrection" to employ more expensive set of parameters specified by option "--rbfeChargeCorrectionParams". A word to the wise: You may want to consider sepecifying "Yes" value for option "--rbfeChargeCorrection".'
  264                 % (MolA.name, MolB.name, ChargeDifference)
  265             )
  266     else:
  267         TransformationProtocol = RBFEProtocol
  268 
  269     return TransformationProtocol
  270 
  271 
  272 def WriteTransformations(MolAToMolBTransformations):
  273     """Write out transformations."""
  274 
  275     TransformationsOutDirPath = pathlib.Path(OptionsInfo["TransformationsOutDirPath"])
  276 
  277     MiscUtil.PrintInfo(
  278         "Writing transformations files (Files: *.json; Count: %s; Subdirectory: %s)..."
  279         % (len(MolAToMolBTransformations), OptionsInfo["TransformationsOutDir"])
  280     )
  281 
  282     for Transformation in MolAToMolBTransformations:
  283         TransformationFilePath = TransformationsOutDirPath.joinpath("%s.json" % Transformation.name)
  284         Transformation.dump(TransformationFilePath)
  285 
  286 
  287 def SetupProtocolDAGs(MolAToMolBTransformations):
  288     """Setup protocol Directed Acyclic Graphs (DAGs) for each transformation to
  289     to perform calculations.
  290     """
  291 
  292     MiscUtil.PrintInfo("\nSetting up protocol DAGs (Count: %s)..." % len(MolAToMolBTransformations))
  293 
  294     MolAToMolBProtocolDAGs = []
  295     for Transformation in MolAToMolBTransformations:
  296         ProtocolDAG = OpenFEUtil.InitializeProtocolDAG(Transformation, Name=Transformation.name)
  297         MolAToMolBProtocolDAGs.append(ProtocolDAG)
  298 
  299     return MolAToMolBProtocolDAGs
  300 
  301 
  302 def ExecuteProtocolDAGsAndGatherResults(MolTransformations, MolProtocolDAGs):
  303     """Execute protocol DAGs and gather results."""
  304 
  305     ResultsSharedOutDirPath = OptionsInfo["ResultsOutDirPath"]
  306     ResultsScratchOutDirPath = OptionsInfo["ResultsScratchOutDirPath"]
  307     ExecuteDAGParams = OptionsInfo["ExecuteDAGParams"]
  308 
  309     MolProtocolResults = OpenFEUtil.ExecuteProtocolDAGsAndGatherResults(
  310         MolTransformations,
  311         MolProtocolDAGs,
  312         ResultsSharedOutDirPath,
  313         ResultsScratchOutDirPath,
  314         KeepShared=ExecuteDAGParams["KeepShared"],
  315         KeepScratch=ExecuteDAGParams["KeepScratch"],
  316         NRetries=ExecuteDAGParams["NRetries"],
  317         WriteResults=True,
  318     )
  319 
  320     return MolProtocolResults
  321 
  322 
  323 def ProcessProtocolResults(MolAToMolBTransformations, MolAToMolBProtocolResults):
  324     """Process protocol results."""
  325 
  326     ResultFileParams = OptionsInfo["ResultFileParams"]
  327 
  328     ResultFile = "%s_RBFE_Results.%s" % (OptionsInfo["OutfilePrefix"], ResultFileParams["Ext"])
  329     ResultFilePath = os.path.join(OptionsInfo["OutfileDirPath"], ResultFile)
  330     MiscUtil.PrintInfo("\nWriting %s..." % ResultFile)
  331 
  332     Precision = ResultFileParams["Precision"]
  333 
  334     ResultData = []
  335     for Index in range(0, len(MolAToMolBProtocolResults), 2):
  336         MolAToMolBComplexSolventProtocolResult = MolAToMolBProtocolResults[Index]
  337         MolAToMolBSolventProtocolResult = MolAToMolBProtocolResults[Index + 1]
  338 
  339         # Setup mol names using complex solvent transformation. The solvent transformation
  340         # also contains the same pair of moleules.
  341         MolAToMolBComplexSolventTransformation = MolAToMolBTransformations[Index]
  342 
  343         MolA = MolAToMolBComplexSolventTransformation.stateA.components["ligand"]
  344         MolB = MolAToMolBComplexSolventTransformation.stateB.components["ligand"]
  345         MolAName = MolA.name
  346         MolBName = MolB.name
  347 
  348         if MolAToMolBComplexSolventProtocolResult is None or MolAToMolBSolventProtocolResult is None:
  349             DeltaDeltaGBinding = "NA"
  350             DeltaDeltaGBindingUncertainty = "NA"
  351         else:
  352             # Setup binding value without the units...
  353             MolAToMolBComplexSolventDeltaG = MolAToMolBComplexSolventProtocolResult.get_estimate()
  354             MolAToMolBSolventDeltaG = MolAToMolBSolventProtocolResult.get_estimate()
  355 
  356             DeltaDeltaGBinding = MolAToMolBComplexSolventDeltaG.m - MolAToMolBSolventDeltaG.m
  357             DeltaDeltaGBinding = "%.*f" % (Precision, DeltaDeltaGBinding)
  358 
  359             # Setup uncertainty value without the units...
  360             MolAToMolBComplexSolventDeltaGUncertainty = MolAToMolBComplexSolventProtocolResult.get_uncertainty()
  361             MolAToMolBSolventDeltaGUncertainty = MolAToMolBSolventProtocolResult.get_uncertainty()
  362 
  363             DeltaDeltaGBindingUncertainty = np.sqrt(
  364                 np.sum(np.square([MolAToMolBComplexSolventDeltaGUncertainty.m, MolAToMolBSolventDeltaGUncertainty.m]))
  365             )
  366             DeltaDeltaGBindingUncertainty = "%.*f" % (Precision, DeltaDeltaGBindingUncertainty)
  367 
  368         ResultData.append([MolAName, MolBName, DeltaDeltaGBinding, DeltaDeltaGBindingUncertainty])
  369 
  370     ResultDF = pd.DataFrame(
  371         ResultData,
  372         columns=["MolAName", "MolBName", "DeltaDeltaG (MolA->MolB; RBFE) (kcal/mol)", "Uncertainty (kcal/mol)"],
  373     )
  374     ResultDF.to_csv(ResultFilePath, sep=ResultFileParams["Delim"], lineterminator="\n", index=False)
  375 
  376 
  377 def GenerateAtomMappings(Mols):
  378     """Generate atom mappings."""
  379 
  380     MiscUtil.PrintInfo("\nChanging directory to %s..." % OptionsInfo["OutfileDir"])
  381     os.chdir(OptionsInfo["OutfileDirPath"])
  382 
  383     # Initialize atom mappers...
  384     MiscUtil.PrintInfo("\nInitializing atom mappers (%s)..." % " ".join(OptionsInfo["MapperList"]))
  385     Mappers = OpenFEUtil.InitializeAtomMappers(OptionsInfo["MapperList"], OptionsInfo["MapperParams"])
  386 
  387     MiscUtil.PrintInfo("\nInitializing atom mapper scorer (%s)..." % OptionsInfo["MapperScorer"])
  388     MapperScorer = OpenFEUtil.InitializeAtomMapperScorer(OptionsInfo["MapperScorer"])
  389 
  390     MolAToMolBMappings = None
  391     if OptionsInfo["MoleculePairsMode"]:
  392         MolAToMolBMappings = GenerateAtomMappingsForMoleculePairs(Mols, Mappers, MapperScorer)
  393     elif OptionsInfo["MoleculeNetworkMode"]:
  394         MolAToMolBMappings = GenerateAtomMappingForMoleculeNetwork(Mols, Mappers, MapperScorer)
  395 
  396     if MolAToMolBMappings is None or len(MolAToMolBMappings) == 0:
  397         MiscUtil.PrintError("Failed to generate atom mappings for small molecules.")
  398 
  399     return MolAToMolBMappings
  400 
  401 
  402 def GenerateAtomMappingsForMoleculePairs(Mols, Mappers, MapperScorer):
  403     """Generate atom mappings for molecule pairs."""
  404 
  405     MiscUtil.PrintInfo(
  406         "\nGenerating atom mappings (%s: %d)..." % (OptionsInfo["Mode"], (len(OptionsInfo["MoleculePairsMolList"]) / 2))
  407     )
  408 
  409     # Generate atom mappings...
  410     MolAToMolBMappings = OpenFEUtil.SuggestAtomMappingsForMoleculePairs(
  411         OptionsInfo["MoleculePairsMolList"], Mappers, MapperScorer
  412     )
  413 
  414     # Write out image files...
  415     WriteMoleculePairsOutputFiles(MolAToMolBMappings)
  416 
  417     return MolAToMolBMappings
  418 
  419 
  420 def WriteMoleculePairsOutputFiles(MolAToMolBMappings):
  421     """Write mapping image output files for molecule pairs."""
  422 
  423     if len(MolAToMolBMappings):
  424         MiscUtil.PrintInfo(
  425             "Writing molecule pairs output files (Files: <MolName1>_To_<MolName2>_*.png; Count: %s;  Subdirectory: %s)..."
  426             % (len(MolAToMolBMappings), OptionsInfo["PairImagesOutfileDir"])
  427         )
  428 
  429     for Mapping in MolAToMolBMappings:
  430         PairOutfilePath = SetupMappingImageFilePath("Molecule_Pair", Mapping, OptionsInfo["PairImagesOutfileDirPath"])
  431         OpenFEUtil.WriteMappingImageFile(Mapping, PairOutfilePath)
  432 
  433 
  434 def GenerateAtomMappingForMoleculeNetwork(Mols, Mappers, MapperScorer):
  435     """Setup atom mapping for molecule network."""
  436 
  437     MiscUtil.PrintInfo("\nGenerating atom mappings (%s)..." % OptionsInfo["Mode"])
  438 
  439     # Generate network...
  440     LigandNetwork = OpenFEUtil.GenerateLigandNetwork(
  441         Mols, OptionsInfo["Network"], OptionsInfo["NetworkParams"], Mappers, MapperScorer
  442     )
  443 
  444     # Write out network output files...
  445     WriteMoleculeNetworkOutputFiles(LigandNetwork)
  446 
  447     # Setup mappings...
  448     MolAToMolBMappings = [Edge for Edge in LigandNetwork.edges]
  449 
  450     return MolAToMolBMappings
  451 
  452 
  453 def WriteMoleculeNetworkOutputFiles(LigandNetwork):
  454     """Write out network output files."""
  455 
  456     NetworkName = OptionsInfo["Network"]
  457     MiscUtil.PrintInfo("\nGenerating ligand network (%s)..." % NetworkName)
  458 
  459     # Write out ligand network graphml and image files...
  460     NetworkOutfilePrefix = "%s_Network_%s_Mapper_%s" % (OptionsInfo["OutfilePrefix"], NetworkName, SetupMapperLabel())
  461     GraphMLOutfile = "%s.graphml" % NetworkOutfilePrefix
  462     ImageOutfile = "%s.%s" % (NetworkOutfilePrefix, OptionsInfo["NetworkParams"]["OutputNetworkFormat"])
  463 
  464     GraphMLOutfilePath = os.path.join(OptionsInfo["OutfileDirPath"], GraphMLOutfile)
  465     ImageOutfilePath = os.path.join(OptionsInfo["OutfileDirPath"], ImageOutfile)
  466 
  467     MiscUtil.PrintInfo("Writing %s..." % GraphMLOutfile)
  468     OpenFEUtil.WriteLigandNetworkGraphMLFile(LigandNetwork, GraphMLOutfilePath)
  469 
  470     MiscUtil.PrintInfo("Writing %s..." % ImageOutfile)
  471     OpenFEUtil.WriteLigandNetworkImageFile(LigandNetwork, ImageOutfilePath)
  472 
  473     #  Write out image files for edges...
  474     NetworkEdges = [Edge for Edge in LigandNetwork.edges]
  475     if OptionsInfo["NetworkParams"]["OutputEdges"]:
  476         if len(NetworkEdges):
  477             MiscUtil.PrintInfo(
  478                 "Writing edge output files (Files: <MolName1>_To_<MolName2>_*.png; Count: %s; Subdirectory: %s)..."
  479                 % (len(NetworkEdges), OptionsInfo["EdgeImagesOutfileDir"])
  480             )
  481 
  482         for Edge in NetworkEdges:
  483             EdgeOutfilePath = SetupMappingImageFilePath("Network", Edge, OptionsInfo["EdgeImagesOutfileDirPath"])
  484             OpenFEUtil.WriteMappingImageFile(Edge, EdgeOutfilePath)
  485 
  486 
  487 def SetupMappingImageFilePath(ModeLabel, Mapping, OutfileDirPath):
  488     """Setup mapping image file path."""
  489 
  490     Outfile = "%s_To_%s_%s_Mapper_%s.png" % (
  491         Mapping.componentA.name,
  492         Mapping.componentB.name,
  493         ModeLabel,
  494         SetupMapperLabel(),
  495     )
  496     Outfile = re.sub(" ", "_", Outfile)
  497 
  498     OutfilePath = os.path.join(OutfileDirPath, Outfile)
  499 
  500     return OutfilePath
  501 
  502 
  503 def SetupMapperLabel():
  504     """Setup mapper label."""
  505 
  506     return "_".join(OptionsInfo["MapperList"])
  507 
  508 
  509 def ProcessInputFiles():
  510     """Process input files."""
  511 
  512     # Read PDB file...
  513     MiscUtil.PrintInfo("\nReading PDB file %s..." % OptionsInfo["Infile"])
  514     MacroMol = OpenFEUtil.ReadPDBFile(OptionsInfo["InfilePath"], Name=OptionsInfo["InfileRoot"])
  515 
  516     # Read small molecule input file...
  517     MiscUtil.PrintInfo("\nReading small molecule file %s..." % OptionsInfo["SmallMolFile"])
  518     Mols, MolCount, ValidMolCount = OpenFEUtil.ReadAndValidateMolecules(
  519         OptionsInfo["SmallMolFilePath"], **OptionsInfo["SmallMolFileParams"]
  520     )
  521 
  522     MiscUtil.PrintInfo("\nTotal number of molecules: %d" % MolCount)
  523     MiscUtil.PrintInfo("Number of valid molecules: %d" % ValidMolCount)
  524     MiscUtil.PrintInfo("Number of ignored molecules: %d" % (MolCount - ValidMolCount))
  525 
  526     if ValidMolCount == 0:
  527         MiscUtil.PrintInfo("")
  528         MiscUtil.PrintError("No valid molecules found in small molecule input file.\n")
  529 
  530     if ValidMolCount < 2:
  531         MiscUtil.PrintInfo("")
  532         MiscUtil.PrintError("Small molecule Input file must contain at least 2 molecules.\n")
  533 
  534     return (MacroMol, Mols)
  535 
  536 
  537 def ValidateMoleculeNames(Mols):
  538     """Validate molecule names."""
  539 
  540     if OptionsInfo["MoleculePairsMode"]:
  541         OptionsInfo["MoleculePairsMolList"] = OpenFEUtil.ProcessMoleculePairs(Mols, OptionsInfo["MoleculePairsList"])
  542     elif OptionsInfo["MoleculeNetworkMode"]:
  543         if OptionsInfo["RadialNetworkStatus"]:
  544             OptionsInfo["NetworkParams"]["RadialCentralLigandMol"] = OpenFEUtil.ProcessRadialCentralLigandName(
  545                 Mols, OptionsInfo["NetworkParams"]["RadialCentralLigand"]
  546             )
  547 
  548 
  549 def CheckMissingPartialCharges(Mols):
  550     """Check missing partial charges for small molecules."""
  551 
  552     MiscUtil.PrintInfo("\nChecking missing partial charges for small molecules...")
  553 
  554     MissingChargesMolCount = OpenFEUtil.GetMissingPartialChargesMolCount(Mols)
  555     MiscUtil.PrintInfo("Number of molecules with missing partial charges: %s" % MissingChargesMolCount)
  556 
  557     if MissingChargesMolCount == 0:
  558         return
  559 
  560     if re.match("^Stop$", OptionsInfo["MissingChargeMode"], re.I):
  561         MiscUtil.PrintInfo("")
  562         MiscUtil.PrintError(
  563             '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'
  564         )
  565     else:
  566         MiscUtil.PrintInfo("")
  567         MiscUtil.PrintWarning(
  568             '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'
  569         )
  570 
  571 
  572 def ProcessOutfilePrefixOption():
  573     """Process outfile prefix option."""
  574 
  575     OutfilePrefix = Options["--outfilePrefix"]
  576 
  577     if re.match("^auto$", OutfilePrefix, re.I):
  578         OutfilePrefix = OptionsInfo["SmallMolFileRoot"]
  579 
  580     OptionsInfo["OutfilePrefix"] = OutfilePrefix
  581 
  582 
  583 def ProcessOutfileDirOption():
  584     """Process outfile directory Option."""
  585 
  586     # Setup output directory...
  587     OutfileDir = Options["--outfileDir"]
  588     OutfileDirPath = os.path.abspath(OutfileDir)
  589     if not os.path.exists(OutfileDir):
  590         MiscUtil.PrintInfo("\nCreating output directory %s..." % (OutfileDir))
  591         os.mkdir(OutfileDirPath)
  592     OptionsInfo["OutfileDir"] = OutfileDir
  593     OptionsInfo["OutfileDirPath"] = OutfileDirPath
  594 
  595     # Setup a images subdirectory for a network...
  596     EdgeImagesOutfileDir = "NetworkEdgeImages"
  597     EdgeImagesOutfileDirPath = os.path.join(OptionsInfo["OutfileDirPath"], EdgeImagesOutfileDir)
  598     if OptionsInfo["MoleculeNetworkMode"] and OptionsInfo["NetworkParams"]["OutputEdges"]:
  599         if not os.path.exists(EdgeImagesOutfileDirPath):
  600             os.mkdir(EdgeImagesOutfileDirPath)
  601     OptionsInfo["EdgeImagesOutfileDir"] = EdgeImagesOutfileDir
  602     OptionsInfo["EdgeImagesOutfileDirPath"] = EdgeImagesOutfileDirPath
  603 
  604     # Setup a images subdirectory for molecule pairs...
  605     PairImagesOutfileDir = "MoleculePairImages"
  606     PairImagesOutfileDirPath = os.path.join(OptionsInfo["OutfileDirPath"], PairImagesOutfileDir)
  607     if OptionsInfo["MoleculePairsMode"]:
  608         if not os.path.exists(PairImagesOutfileDirPath):
  609             os.mkdir(PairImagesOutfileDirPath)
  610     OptionsInfo["PairImagesOutfileDir"] = PairImagesOutfileDir
  611     OptionsInfo["PairImagesOutfileDirPath"] = PairImagesOutfileDirPath
  612 
  613     # Setup a transformations subdirectory...
  614     TransformationsOutDir = "Transformations"
  615     TransformationsOutDirPath = os.path.join(OptionsInfo["OutfileDirPath"], TransformationsOutDir)
  616     if not os.path.exists(TransformationsOutDirPath):
  617         os.mkdir(TransformationsOutDirPath)
  618     OptionsInfo["TransformationsOutDir"] = TransformationsOutDir
  619     OptionsInfo["TransformationsOutDirPath"] = TransformationsOutDirPath
  620 
  621     # Setup a results subdirectory...
  622     ResultsOutDir = "Results"
  623     ResultsOutDirPath = os.path.join(OptionsInfo["OutfileDirPath"], ResultsOutDir)
  624     if not os.path.exists(ResultsOutDirPath):
  625         os.mkdir(ResultsOutDirPath)
  626     OptionsInfo["ResultsOutDir"] = ResultsOutDir
  627     OptionsInfo["ResultsOutDirPath"] = ResultsOutDirPath
  628 
  629     # Use results subdirectory for scratch results...
  630     OptionsInfo["ResultsScratchOutDir"] = ResultsOutDir
  631     OptionsInfo["ResultsScratchOutDirPath"] = ResultsOutDirPath
  632 
  633 
  634 def ProcessListOption():
  635     """Process list protocol settings option."""
  636 
  637     RBFESettings = openfe.protocols.openmm_rfe.RelativeHybridTopologyProtocol.default_settings()
  638 
  639     MiscUtil.PrintInfo("\nListing RBFE settings...")
  640     OpenFEUtil.ListOpenFESettings(RBFESettings)
  641 
  642 
  643 def ConfigureLogging():
  644     """Configure logging."""
  645 
  646     OptionsInfo["LoggingLevel"] = Options["--loggingLevel"]
  647 
  648     if re.match("^Error$", OptionsInfo["LoggingLevel"], re.I):
  649         LoggingLevel = logging.ERROR
  650     elif re.match("^Warning$", OptionsInfo["LoggingLevel"], re.I):
  651         LoggingLevel = logging.WARNING
  652     else:
  653         LoggingLevel = logging.INFO
  654 
  655     logging.basicConfig(format="%(levelname)s: %(message)s", level=LoggingLevel)
  656 
  657     # Turn warnings issued by warnings.warn() into log message to avoid display
  658     # of a stack trace...
  659     logging.captureWarnings(True)
  660 
  661 
  662 def ProcessOptions():
  663     """Process and validate command line arguments and options."""
  664 
  665     MiscUtil.PrintInfo("Processing options...")
  666 
  667     # Validate options...
  668     ValidateOptions()
  669 
  670     # Configure logging...
  671     ConfigureLogging()
  672 
  673     OptionsInfo["Infile"] = Options["--infile"]
  674     OptionsInfo["InfilePath"] = os.path.abspath(OptionsInfo["Infile"])
  675     FileDir, FileName, FileExt = MiscUtil.ParseFileName(OptionsInfo["Infile"])
  676     OptionsInfo["InfileRoot"] = FileName
  677 
  678     OptionsInfo["SmallMolFile"] = Options["--smallMolFile"]
  679     OptionsInfo["SmallMolFilePath"] = os.path.abspath(OptionsInfo["SmallMolFile"])
  680     FileDir, FileName, FileExt = MiscUtil.ParseFileName(OptionsInfo["SmallMolFile"])
  681     OptionsInfo["SmallMolFileRoot"] = FileName
  682 
  683     ParamsDefaultInfoOverride = {"RemoveHydrogens": False}
  684     OptionsInfo["SmallMolFileParams"] = MiscUtil.ProcessOptionInfileParameters(
  685         "--smallMolFileParams",
  686         Options["--smallMolFileParams"],
  687         InfileName=Options["--smallMolFile"],
  688         ParamsDefaultInfo=ParamsDefaultInfoOverride,
  689     )
  690 
  691     OptionsInfo["ExecuteDAGParams"] = OpenFEUtil.ProcessOptionOpenFEExecuteDAGParameters(
  692         "--executeDAGParams", Options["--executeDAGParams"]
  693     )
  694 
  695     OptionsInfo["LoggingLevel"] = Options["--loggingLevel"]
  696 
  697     OptionsInfo["MapperList"] = OpenFEUtil.ProcessOptionOpenFEMapper("-m, --mapper", Options["--mapper"])
  698     OptionsInfo["MapperParams"] = OpenFEUtil.ProcessOptionOpenFEMapperParameters(
  699         "-m, --mapperParams", Options["--mapperParams"]
  700     )
  701     OptionsInfo["MapperScorer"] = Options["--mapperScorer"]
  702 
  703     OptionsInfo["Mode"] = OpenFEUtil.ProcessOptionOpenFERelativeFreeEnergyMode("-m, --mode", Options["--mode"])
  704     OptionsInfo["MoleculePairsMode"] = True if re.match("^MoleculePairs$", OptionsInfo["Mode"], re.I) else False
  705     OptionsInfo["MoleculeNetworkMode"] = True if re.match("^MoleculeNetwork$", OptionsInfo["Mode"], re.I) else False
  706 
  707     OptionsInfo["MissingChargeMode"] = OpenFEUtil.ProcessOptionOpenFEMissingChargeMode(
  708         "--missingChargeMode", Options["--missingChargeMode"]
  709     )
  710 
  711     OptionsInfo["Network"] = OpenFEUtil.ProcessOptionOpenFENetwork("-n, --network", Options["--network"])
  712     OptionsInfo["RadialNetworkStatus"] = True if re.match("^Radial$", OptionsInfo["Network"], re.I) else False
  713 
  714     ParamsDefaultInfoOverride = {"OutputEdges": True}
  715     OptionsInfo["NetworkParams"] = OpenFEUtil.ProcessOptionOpenFENetworkParameters(
  716         "--networkParams",
  717         Options["--networkParams"],
  718         RadialNetworkStatus=OptionsInfo["RadialNetworkStatus"],
  719         ParamsDefaultInfo=ParamsDefaultInfoOverride,
  720     )
  721 
  722     OptionsInfo["MoleculePairs"] = Options["--moleculePairs"]
  723     OptionsInfo["MoleculePairsList"] = OpenFEUtil.ProcessOptionOpenFEMoleculePairs(
  724         "--moleculePairs", Options["--moleculePairs"]
  725     )
  726     OptionsInfo["MoleculePairsMolList"] = None
  727 
  728     OptionsInfo["ResultFileParams"] = OpenFEUtil.ProcessOptionOpenFEResultFileParameters(
  729         "--resultFileParams", Options["--resultFileParams"]
  730     )
  731 
  732     ParamsDefaultInfoOverride = {"EngineComputePlatform": "CPU"}
  733     OptionsInfo["RBFEParams"] = OpenFEUtil.ProcessOptionOpenFERelativeFreeEnergyParameters(
  734         "--rbfeParams", Options["--rbfeParams"], ParamsDefaultInfo=ParamsDefaultInfoOverride
  735     )
  736 
  737     OptionsInfo["RBFEChargeCorrection"] = True if re.match("^yes$", Options["--rbfeChargeCorrection"]) else False
  738     OptionsInfo["RBFEChargeCorrectionParams"] = (
  739         OpenFEUtil.ProcessOptionOpenFERelativeFreeEnergyChargeCorrectionParameters(
  740             "--rbfeChargeCorrectionParams", Options["--rbfeChargeCorrectionParams"]
  741         )
  742     )
  743 
  744     OptionsInfo["SolventParams"] = OpenFEUtil.ProcessOptionOpenFESolventParameters(
  745         "--solventParams", Options["--solventParams"]
  746     )
  747 
  748     ProcessOutfilePrefixOption()
  749     ProcessOutfileDirOption()
  750 
  751     OptionsInfo["Overwrite"] = Options["--overwrite"]
  752 
  753     # Track top level working directory...
  754     OptionsInfo["TopWorkingDir"] = os.getcwd()
  755 
  756 
  757 def RetrieveOptions():
  758     """Retrieve command line arguments and options."""
  759 
  760     # Get options...
  761     global Options
  762     Options = docopt(_docoptUsage_)
  763 
  764     # Set current working directory to the specified directory...
  765     WorkingDir = Options["--workingdir"]
  766     if WorkingDir:
  767         os.chdir(WorkingDir)
  768 
  769     # Handle examples option...
  770     if "--examples" in Options and Options["--examples"]:
  771         MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_))
  772         sys.exit(0)
  773 
  774 
  775 def ValidateOptions():
  776     """Validate option values."""
  777 
  778     MiscUtil.ValidateOptionFilePath("-i, --infile", Options["--infile"])
  779     MiscUtil.ValidateOptionFileExt("-i, --infile", Options["--infile"], "pdb cif")
  780 
  781     MiscUtil.ValidateOptionFilePath("-s, --smallMolFile", Options["--smallMolFile"])
  782     MiscUtil.ValidateOptionFileExt("-s, --smallMolFile", Options["--smallMolFile"], "sdf sd")
  783 
  784     MiscUtil.ValidateOptionDirPath("-o, --outfileDir", Options["--outfileDir"])
  785     MiscUtil.ValidateOptionsOutputDirOverwrite(
  786         "-o, --outfileDir", Options["--outfileDir"], "--overwrite", Options["--overwrite"]
  787     )
  788 
  789     MiscUtil.ValidateOptionTextValue("--loggingLevel", Options["--loggingLevel"], "Info Warning Error")
  790 
  791     for Mapper in Options["--mapper"].split(","):
  792         Mapper = Mapper.strip()
  793         MiscUtil.ValidateOptionTextValue("--mapper", Mapper, "LOMAP Kartograf")
  794 
  795     MiscUtil.ValidateOptionTextValue("--mapperScorer", Options["--mapperScorer"], "LOMAP")
  796 
  797     MiscUtil.ValidateOptionTextValue("-m, --mode", Options["--mode"], "MoleculePairs MoleculeNetwork")
  798     MiscUtil.ValidateOptionTextValue("--missingChargeMode", Options["--missingChargeMode"], "Calculate Stop")
  799 
  800     MoleculePairs = Options["--moleculePairs"]
  801     if not re.match("^auto$", MoleculePairs, re.I):
  802         MoleculePairsList = MoleculePairs.split(",")
  803         if len(MoleculePairsList) % 2:
  804             MiscUtil.PrintError(
  805                 'The number of comma delimited values, %d, specified using "--moleculePairs" option must be an even number.'
  806                 % (len(MoleculePairsList))
  807             )
  808 
  809     MiscUtil.ValidateOptionTextValue("-n, --network", Options["--network"], "LOMAP MinimalSpanning Radial")
  810 
  811     MiscUtil.ValidateOptionTextValue("--rbfeChargeCorrection", Options["--rbfeChargeCorrection"], "yes no")
  812 
  813 
  814 # Setup a usage string for docopt...
  815 _docoptUsage_ = """
  816 OpenFECalculateRelativeBindingFreeEnergy.py - Calculate relative binding free energy
  817 
  818 Usage:
  819     OpenFECalculateRelativeBindingFreeEnergy.py [--executeDAGParams <Name,Value,..>] [--loggingLevel <Info, Warning or Error>]
  820                                                 [--mapper <mapper1, mapper2,...>] [--mapperParams <Name,Value,..>] [--mapperScorer <LOMAP>]
  821                                                 [--mode <MoleculePairs or MoleculeNetwork>] [--missingChargeMode <Calculate or Stop>]
  822                                                 [--moleculePairs <MolName1,MolName2,..>] [--network <text>] [--networkParams <Name,Value,..>]
  823                                                 [--outfilePrefix <text>] [--overwrite] [--rbfeParams <Name,Value,...>] [--rbfeChargeCorrection <yes or no>]
  824                                                 [--rbfeChargeCorrectionParams <Name,Value,...>] [--resultFileParams <Name,Value,..>]
  825                                                 [--solventParams <Name,Value,...>] [--smallMolFileParams <Name,Value,...> ]
  826                                                 [-w <dir>] -i <infile> -s <smallmolfile> -o <outifiledir>
  827     OpenFECalculateRelativeBindingFreeEnergy.py -l | --list
  828     OpenFECalculateRelativeBindingFreeEnergy.py -h | --help | -e | --examples
  829 
  830 Description:
  831     Calculate Relative Binding Free Energy (RBFE) for a pair of molecules in a
  832     small molecule input file. You may calculate RBFEs for specific pairs of
  833     molecules or all molecule pairs corresponding to edges in a molecule network.
  834 
  835     The input file must contain a macromolecule already prepared for simulation.
  836     The preparation of the macromolecule for a simulation generally involves the
  837     following tasks: identification and replacement of non-standard residues;
  838     addition of missing residues; addition of missing heavy atoms; addition of
  839     missing hydrogens.
  840 
  841     In addition, the small molecule input file must contain molecules already
  842     prepared for simulation. It must contain appropriate 3D coordinates relative
  843     to the macromolecule along with no missing hydrogens.
  844 
  845     The MD simulation workflow, employed for the calculation of RBFEs, involves the
  846     following steps: initial minimization; NVT equilibration; NPT equilibration;
  847     production NPT. The MD simulation protocol is repeated 3 times for each pair
  848     pair of transformations, MolAToMolBComplexSolvent and MolAToMolBSolvent,
  849     and the results are analyzed to estimate RBFEs. The default time and step size
  850     settings for the MD protocol are shown below:
  851         
  852         Protocol repeats, 3
  853         
  854         Time step size: 4.0 femtosecond
  855         
  856         Max minimization steps: 5,000
  857         
  858         NVT equilibration length: 1.0 nanosecond
  859         NPT equilibration length: 1.0 nanosecond
  860         NPT production length: 5.0 nanosecond
  861         
  862     Each MD simulation, by default, may run for 7 nanosecond, for a total of 21
  863     nanosecond to repeat it 3 times. The total MD simulation time for each pair
  864     of transformations, MolAToMolBComplexSolvent and MolAToMolBSolvent,
  865     may correspond to more than 42 nanoseconds.
  866 
  867     The supported macromolecule input file formats are:  PDB (.pdb) and
  868     CIF (.cif)
  869 
  870     The supported small molecule input file format are : SD (.sdf, .sd)
  871 
  872     Possible outfile prefix:
  873         
  874         <OutfilePrefix> or <SmallMolFileRoot>
  875         
  876     Possible output directories:
  877         
  878         <OutfileDir>
  879         
  880         <OutfileDir>/MoleculePairImages [ MoleculeNetwork mode ]
  881         <OutfileDir>/NetworkEdgeImages [ MoleculePairs mode]
  882         
  883         <OutfileDir>/Transformations
  884         <OutfileDir>/Results
  885         
  886     Possible output files and directories under <OutfileDir>:
  887         
  888         <OutfilePrefix>_RBFE_Results.<csv or tsv>
  889         
  890         MoleculePairImages/<MolAName>_To_<MolBName>_Molecule_Pair*.png
  891         ... ... ...
  892          
  893         <OutfilePrefix>_Network*.graphml
  894         <OutfilePrefix>_Network*.svg
  895         NetworkEdgeImages/<MolAName>_To_<MolBName>_Network*.png
  896         ... ... ...
  897         
  898         Transformations/<MolAName>_To_<MolBName>_Complex_Solvent.json
  899         Transformations/<MolAName>_To_<MolBName>_Solvent.json
  900         ... ... ...
  901         
  902         Results/shared_RelativeHybridTopologyProtocolUnit-*/
  903         Results/scratch_RelativeHybridTopologyProtocolUnit-*/
  904         ... ... ...
  905 
  906 Options:
  907     -e, --examples
  908         Print examples.
  909     --executeDAGParams <Name,Value,..>  [default: auto]
  910         A comma delimited list of parameter name and value pairs for executing
  911         protocol DAGs (Directed Acyclic Graph) to run RBFE calculations.
  912         
  913         The supported parameter names along with their default values are
  914         are shown below:
  915             
  916             keepShared, yes  [ Possible values: yes or no ]
  917             keepScratch, no  [ Possible values: yes or no ]
  918             nRetries, 2  [ Possible values: >= 0. A value of 0 implies only
  919                 1 try. ]
  920             
  921         A brief description of parameters is provided below:
  922             
  923             keepShared: Keep shared directories after the execution of DAG.
  924             keepScratch: Keep scratch directories after the execution of DAG.
  925             nRetries: Number of times to attempt the execution.
  926             
  927     -h, --help
  928         Print this help message.
  929     -i, --infile <infile>
  930         Input file name containing a macromolecule.
  931     -l, --list
  932         List default RBFE protocol settings provided by OpenFE module
  933         RelativeHybridTopologyProtocol.
  934     --loggingLevel <Info, Warning or Error>  [default: Error]
  935         Logging level to configure the 'root logger' via logging.basicConfig()
  936         function. The default logging level is changed from 'logging.INFO' to
  937         'logging.ERROR'. Otherwise, OpenFE and its associated modules
  938         may generate a lot of informational messages.
  939     --mapper <mapper1, mapper2>  [default: LOMAP]
  940         A comma delimited names of atom mappers for generating atom mapping
  941         corresponding to molecule pairs or edges in a molecule network. Possible
  942         values: LOMAP [ Lead Optimization MAPer; Ref 176 ] or Kartograf [ Ref 177 ].
  943         You may specify multiple mappers for generating mapping between pair of
  944         molecules. All specified mappers are employed to identify the highest
  945         scoring mapping between a pair of molecules.
  946     --mapperParams <Name,Value,..>  [default: auto]
  947         A comma delimited list of parameter name and value pairs for atom mappers
  948         employed to generate mapping between molecule pairs or edges in a molecule
  949         network. 
  950         
  951         The supported parameter names along with their default values are
  952         are shown below:
  953             
  954             lomapTime, 20, [ Units: seconds ]
  955             lomapThreeD, yes [ Possible values: yes or no ]
  956             lomapMax3D, 1.0 [ Units: Angstrom ]
  957             lomapElementChange, yes [ Possible values: yes or no]
  958             lomapSeed, None [ Possible value: A string. An empty string causes
  959                 MCS search to start from scratch ]
  960             lomapShift, no [  Possible values: yes or no]
  961             
  962             kartografAtomMaxDistance, 0.95 [ Units: Angstrom ]
  963             kartografAtomMapHydrogens, yes [ Possible values: yes or no ]
  964             kartografMapHydrogensOnHydrogensOnly, No [ Possible values: yes or
  965                 no ]
  966             kartografMapExactRingMatchesOnly, yes [ Possible values: yes or no ]
  967             kartografAllowPartialFusedRings, yes [ Possible values: yes or no ]
  968             
  969         A brief description of parameters is provided below:
  970             
  971             lomapTime: Time out for MCS algorithm.
  972             lomapThreeD: Use atom positions to prune symmetric mappings.
  973             lomapMax3D: Forbid mapping between atoms with distance more than
  974                 specified value.
  975             lomapElementChange: Allow mappings that change an atom element.
  976             lomapSeed: An Empty SMARTS string causes MCS search to start from
  977                 scratch.
  978             lomapShift: Keep pre-aligned atom positions for 3D position checks.
  979             
  980             kartografAtomMaxDistance: Geometric criteria for two atoms
  981                 corresponding to maximum distance between them.
  982             kartografAtomMapHydrogens: Map hydrogens.
  983             kartografMapHydrogensOnHydrogensOnly: Map hydrogens only on
  984                 hydrogens.
  985             kartografMapExactRingMatchesOnly: Map rings with only matching ring
  986                 size and bond orders. In addition, ring breaking is not
  987                 permitted.
  988             kartografAllowPartialFusedRings: Allow mapping of partially fused
  989                 rings.
  990             
  991     --mapperScorer <LOMAP>  [default: LOMAP]
  992         Atom mapper scorer to use for scoring mapping between molecule pairs or
  993         edges in a molecule network. Possible value: LOMAP. The atom scorer is
  994         not used during the generation of MinimalSpanning network.
  995     -m, --mode <MoleculePairs or MoleculeNetwork>  [default: MoleculePairs]
  996         Calculate RBFEs for specified pairs of molecules or all molecule pairs
  997         corresponding to edges in a molecule network.
  998     --missingChargeMode <Calculate or Stop>  [default: Stop]
  999         Calculate missing partial charges for molecules before running RBFE
 1000         calculations or terminate the execution of the script. The missing
 1001         partial charges will be automatically calculated by OpenFE module
 1002         RelativeHybridTopologyProtocol during the calculation of RBFE. You
 1003         may control the calculation of partial charges by specifying values for
 1004         partialCharge* parameters using '--rbfeParams' option.
 1005     --moleculePairs <MolName1,MolName2,..>  [default: auto]
 1006         A comma delimited list of molecule name pairs for calculating RBFEs.
 1007         Default: the names of the first and second molecule in small molecule
 1008         input file. This option is only used during 'MoleculePairs' value for
 1009         '-m, --mode' option. 
 1010     -n, --network <text>  [default: MinimalSpanning]
 1011         Name of a molecule network to generate for calculating RBFEs. Possible
 1012         values: LOMAP, MinimalSpanning or Radial. This option is only used during
 1013         'MoleculeNetwork' value for '-m, --mode' option. 
 1014     --networkParams <Name,Value,..>  [default: auto]
 1015         A comma delimited list of parameter name and value pairs for generating
 1016         a molecule network.
 1017         
 1018         The supported parameter names along with their default values are
 1019         are shown below:
 1020             
 1021             lomapDistanceCutoff, 0.4
 1022             lomapMaxPathLength, 6
 1023             lomapRequireCycleCovering, yes  [ Possible values: yes or no ]
 1024             
 1025             minimalSpanningProgress, no  [ Possible values: yes or no ]
 1026             
 1027             radialCentralLigand, None  [ Possible values: Valid ligand name ]
 1028             
 1029             outputEdges, no  [ Possible values: yes or no ]
 1030             outputNetworkFormat, svg  [ Possible values: Any valid format. ]
 1031             
 1032         A brief description of parameters is provided below:
 1033             
 1034             lomapDistanceCutoff: Maximum distance/dissimilarity between two
 1035                 molecules for an edge to be accepted.
 1036             lomapMaxPathLength: Maximum distance between any two molecules in
 1037                 the resulting network
 1038             lomapRequireCycleCovering: Add cycles into the network
 1039             
 1040             minimalSpanningProgress: Show progress using tqdm.
 1041             
 1042             radialCentralLigand: Name of central ligand. A valid ligand name
 1043                 must be specified to generate a radial molecule network.
 1044             
 1045             outputEdges: Generate PNG image files for all edges in a molecule
 1046                 network.
 1047             outputNetworkFormat: Valid image file format for molecule network.
 1048                 You must specify a valid format supported by Python module
 1049                 Matplotlib. For example: PNG (.png), SVG (.svg), PDF (.pdf),
 1050                 etc. In addition, the graphml file is always generated.
 1051             
 1052     -o, --outfileDir <outfiledir>
 1053         Output directory.
 1054     --outfilePrefix <text>  [default: auto]
 1055         Prefix for generating output files under output directory.
 1056     --overwrite
 1057         Overwrite existing files.
 1058     --resultFileParams <Name,Value,..>  [default: auto]
 1059         A comma delimited list of parameter name and value pairs for writing
 1060         calculated RBFEs values to a results file.
 1061         
 1062         The supported parameter names along with their default values are
 1063         are shown below:
 1064             
 1065             precision, 4  [ Possible values: > 0 ]
 1066             delimiter, comma  [ Possible values: comma or tab ]
 1067             
 1068     -r, --rbfeParams <Name,Value,...>  [default: auto]
 1069         A comma delimited list of parameter name and value pairs for RBFE protocol
 1070         settings employed during the calculation of RBFEs.
 1071         
 1072         The default values are automatically updated to match settings provided by
 1073         OpenFE module RelativeHybridTopologyProtocol.
 1074         
 1075         You must specify valid OpenFE values for these parameters. An extensive
 1076         validation is not performed.
 1077         
 1078         The supported parameter names along with their default values are
 1079         are shown below:
 1080             
 1081             protocolRepeats, 3
 1082             
 1083             Alchemical settings:
 1084             
 1085             alchemicalEndstateDispersionCorrection, no  [ Possible values:
 1086                 yes or no ]
 1087             alchemicalExplicitChargeCorrection, no  [ Possible values:
 1088                 yes or no ]
 1089             alchemicalExplicitChargeCorrectionCutoff, 0.8  [ Units: nanometer ]
 1090             alchemicalSoftcoreLJ, Gapsys [ Possible values: Gapsys or Beutler ] 
 1091             alchemicalSoftcoreAlpha, 0.85
 1092             alchemicalTurnOffCoreUniqueExceptions, no  [ Possible values:
 1093                 yes or no ]
 1094             alchemicalUseDispersionCorrection, no [ Possible values: yes or no ]
 1095             
 1096             Engine settings:
 1097             
 1098             engineComputePlatform, CPU  [ Possible values: CPU, CUDA, OpenCL,
 1099                 or Reference ]
 1100             engineGpuDeviceIndex, None [ Possible values: 0, 0 1, etc. ]
 1101             
 1102             Forcefield settings:
 1103             
 1104             forcefieldConstraints, HBonds  [ Possible values: HBonds, AllBonds, or
 1105                 HAngles  ]
 1106             forcefields, amber/ff14SB.xml amber/tip3p_standard.xml
 1107                 amber/tip3p_HFE_multivalent.xml amber/phosaa10.xml
 1108                 [ Possible values: A space delimited list of valid names. ]
 1109             forcefieldHydrogenMass, 3.0  [ Units: amu ]y
 1110             forcefieldNonbondedCutoff, 0.9  [ Units: nanometer ]
 1111             forcefieldNonbondedMethod, PME [ Possible values: PME or NoCutoff ]
 1112             forcefieldRigidWater, yes  [ Possible values: yes or no ]
 1113             forcefieldSmallMoleculeForcefield, openff-2.1.1  [ Possible value:
 1114                 A valid forcefield name. ]
 1115             
 1116             Integrator settings:
 1117             
 1118             integratorBarostatFrequency, 25.0 * timestep  [ The specified value
 1119                 is a multiple of integratorTimestep. ]
 1120             integratorConstraintTolerance, 1e-06
 1121             integratorLangevinCollisionRate, 1.0  [ Units: 1 / picosecond ]
 1122             integratorNRestartAttempts, 20
 1123             integratorReassignVelocities, no  [ Possible values: yes or no ]
 1124             integratorRemoveCom, no  [ Possible values: yes or no ]
 1125             integratorTimestep, 4.0 [ Units: femtosecond ] 
 1126             
 1127             Lambda settings:
 1128             
 1129             lambdaFunctions, default  [ Possible values: Default, namd, or
 1130                 quarters ]
 1131             lambdaWindows, 11
 1132             
 1133             Output settings:
 1134             
 1135             outputCheckpointInterval, 1.0 [ Units: nanosecond ]
 1136             outputCheckpointStorageFilename, checkpoint.chk
 1137             outputForcefieldCache, db.json
 1138             outputFilename, simulation.nc
 1139             outputIndices, not water  [ Possible value: Any valid selection. ]
 1140             outputStructure, hybrid_system.pdb
 1141             outputPositionsWriteFrequency, 100.0 [ Units: picosecond ]
 1142             outputVelocitiesWriteFrequency, None  [  Possible values: > 0;
 1143                 Units: picosecond ]
 1144             
 1145             Partial charge settings:
 1146             
 1147             partialChargeNaglModel, None  [ Default: Production AM1BCC model for
 1148                 NAGL; Possible value: Any valid name. ]
 1149             partialChargeNumberOfConformers, None  [ Possible value: > 0 ]
 1150             partialChargeOffToolkitBackend, AmberTools  [ Possible values:
 1151                 AmberTools or RDKit ]
 1152             partialChargeMethod, AM1BCC  [ Possble values: AM1BCC, Espaloma,
 1153                 or NAGL ]
 1154             
 1155             Simulation settings:
 1156             
 1157             simulationEarlyTerminationTargetError, 0.0 [ Units:
 1158                 kilocalorie_per_mole ]
 1159             simulationEquilibrationLength, 1.0 [ Units: nanosecond ]
 1160             simulationMinimizationSteps, 5000
 1161             simulationNReplicas, 11
 1162             simulationProductionLength, 5.0 [ Units: nanosecond ]
 1163             simulationRealTimeAnalysisInterval, 250.0 [ Units: picosecond ]
 1164             simulationRealTimeAnalysisMinimumTime, 500.0  [ Units: picosecond ]
 1165             simulationSamplerMethod, repex  [ Possible values: repex, sams,
 1166                 or independent ]
 1167             simulationSamsFlatnessCriteria, logZ-flatness  [ Possible values:
 1168                 logZ-flatness, minimum-visits or histogram-flatness ]
 1169             simulationSamsGamma0, 1.0
 1170             simulationTimePerIteration, 2.5  [ Units: picosecond ]
 1171             
 1172             Solvation settings:
 1173             
 1174             solvationBoxShape, dodecahedron  [  Possible values: cube,
 1175                 dodecahedron, or octahedron ]
 1176             solvationBoxSize, None  [ Possible value: A triplet of space
 1177                 X Y Z values; Units: nanometer ]
 1178             solvationSolventModel, tip3p  [ Possible values: tip3p, spce, tip4pew,
 1179                 or tip5p ]
 1180             solvationSolventPadding, 1.5  [ Units: nanometer ]
 1181             
 1182             Thermo settings:
 1183             
 1184             thermoPh, None  [ Possible values: > 0 ]
 1185             thermoPressure, 1.0  [ Units: bar ]
 1186             thermoRedoxPotential, None  [ Possible values: A valid float.
 1187                 Units: millivolts (mV) ]
 1188             thermoTemperature, 298.15  [ Units: kelvin ]
 1189             
 1190         A brief description of parameters, taken from OpenFE documentation, is
 1191         provided below:
 1192             
 1193             protocolRepeats: Number of completely independent repeats of the
 1194                 entire sampling process.
 1195             
 1196             Alchemical settings:
 1197             
 1198             Parameters controlling the creation of the hybrid topology system,
 1199             including various parameters ranging from softcore parameters to
 1200             whether or not to apply an explicit charge correction for systems
 1201             with net charge changes.
 1202             
 1203             alchemicalEndstateDispersionCorrection: Employ extra unsampled
 1204                 endstate windows for long range correction.
 1205             alchemicalExplicitChargeCorrection: Explicitly account for a charge
 1206                 difference during the alchemical transformation by transforming
 1207                 a water to a counterion of the opposite charge of the formal
 1208                 charge difference.
 1209             alchemicalExplicitChargeCorrectionCutoff: Minimum distance from the
 1210                 system solutes from which an alchemical water can be chosen.
 1211             alchemicalSoftcoreLJ: Use LJ softcore function as defined by Gapsys
 1212                 [ Ref 181 ] or Buetler [ Ref 182 ].
 1213             alchemicalSoftcoreAlpha: Softcore alpha parameter.
 1214             alchemicalTurnOffCoreUniqueExceptions: Turn off interactions for
 1215                 new exceptions (not just 1,4s) at lambda 0 and old exceptions at
 1216                 lambda 1 between unique atoms and core atoms.
 1217             alchemicalUseDispersionCorrection: Use dispersion correction in the
 1218                 hybrid topology state.
 1219         
 1220             Engine settings:
 1221             
 1222             Parameters configuring the compute platform used by the OpenMM to
 1223             perform the simulation.
 1224             
 1225             engineComputePlatform: Platform to use for running OpenMM MD
 1226                 calculations.
 1227             engineGpuDeviceIndex: Space delimited list of device indices to use
 1228                 for running OpenMM MD calculations.
 1229             
 1230             Forcefield settings:
 1231             
 1232             Parameters to set up the force field with OpenMM Force Fields,
 1233             including the general force fields, the small molecule force field,
 1234             the nonbonded method, and the nonbonded cutoff.
 1235             
 1236             forcefieldConstraints: Constraints to use.
 1237             forcefields: List of valid forcefield paths for all components
 1238                 except small molecules.
 1239             forcefieldHydrogenMass: Mass to be repartitioned to hydrogens from
 1240                 neighboring heavy atoms.
 1241             forcefieldNonbondedCutoff: Cutoff for short range nonbonded
 1242                 interactions.
 1243             forcefieldNonbondedMethod: Method for treating nonbonded
 1244                 interactions.
 1245             forcefieldRigidWater: Use a rigid water model.
 1246             forcefieldSmallMoleculeForcefield: A valid forcefield name to use
 1247                 for small molecules.
 1248             
 1249             Integrator settings
 1250             
 1251             Parameters controlling the LangevinSplittingDynamicsMove integrator
 1252             used for simulation.
 1253             
 1254             integratorBarostatFrequency: Frequency at which volume scaling
 1255                 changes should be attempted.
 1256             integratorConstraintTolerance: Tolerance for constraint solver.
 1257             integratorLangevinCollisionRate: Collision frequency.
 1258             integratorNRestartAttempts: Number of attempts to restart from
 1259                 Context in case there are NaNs in the energies after
 1260                 integration.
 1261             integratorReassignVelocities: Reassign velocities  from the
 1262                 Maxwell-Boltzmann distribution at the beginning of each
 1263                 Monte Carlo move.
 1264             integratorRemoveCom: Remove the center of mass motion.
 1265             integratorTimestep: Size of the simulation timestep.
 1266             
 1267             Lambda settings:
 1268             
 1269             Lambda protocol parameters, including number of lambda windows and
 1270             lambda functions.
 1271             
 1272             lambdaFunctions: Function name to use for alchemical mutation.
 1273             lambdaWindows: Number of lambda windows to calculate.
 1274             
 1275             Output settings:
 1276             
 1277             Parameter controlling simulation output, including the frequency to
 1278             write a checkpoint file, the selection string for writing selected
 1279             coordinates, and the paths to the trajectory and output structure
 1280             files.
 1281             
 1282             outputCheckpointInterval: Frequency to write the checkpoint file.
 1283             outputCheckpointStorageFilename: Checkpoint filename.
 1284             outputForcefieldCache: Filename for caching small molecule residue
 1285                 templates.
 1286             outputFilename: Trajectory filename.
 1287             outputIndices: Selection string for selecting coordinates to write.
 1288             outputStructure: Hybrid topology structure filename.
 1289             outputPositionsWriteFrequency: Frequency for writing positions to
 1290                 trajectory file.
 1291             outputVelocitiesWriteFrequency: Frequency for writing velocities to
 1292                 trajectory file.
 1293             
 1294             Partial charge settings:
 1295             
 1296             Parameters for automatically assigning missing partial charges to
 1297             small molecules, including the partial charge method.
 1298             
 1299             partialChargeNaglModel: Model to use for partial charge assignment.
 1300                 A value of None implies the use of the latest available
 1301                 production AM1BCC model.
 1302             partialChargeNumberOfConformers: Number of conformers to generate
 1303                 as part of the partial charge assignment. A value of None
 1304                 implies the use of the existing conformer.
 1305             partialChargeOffToolkitBackend: OpenFF toolkit registry backend to
 1306                 use for calculating partial charges.
 1307             partialChargeMethod: Method to use for calculating partial charges.
 1308             
 1309             Simulation settings:
 1310             
 1311             Parameters controlling the simulation plan and the alchemical
 1312             sampler, including the number of minimization steps, lengths of
 1313             equilibration and production runs, the sampler method (e.g.
 1314             Hamiltonian REPlica EXchange (repex), and the time interval at
 1315             which to perform an analysis of the free energies.
 1316             
 1317             simulationEarlyTerminationTargetError: Target error for the real
 1318                 time analysis measured in kcal/mol. Once the MBAR error of the
 1319                 free energy is at or below this value, the simulation will be
 1320                 considered complete. The suggested value of 0.12 has shown to
 1321                 be effective in both hydration and binding free energy
 1322                 benchmarks.
 1323             simulationEquilibrationLength: Length of the equilibration phase.
 1324                 The specified value must be divisible by 'integratorTimestep'.
 1325             simulationMinimizationSteps: Maximum number of minimization steps
 1326                 to perform.
 1327             simulationNReplicas: Number of replicas to use.
 1328             simulationProductionLength: Length of the production phase.
 1329                 The specified value must be divisible by 'integratorTimestep'.
 1330             simulationRealTimeAnalysisInterval: Time interval for performing
 1331                 analysis of the free energies. At each interval, real time
 1332                 analysis data will be written to a yaml file named
 1333                 <outputFileName>_real_time_analysis.yaml. The current error
 1334                 in the estimate will also be assessed and the simulation will
 1335                 be terminated when it drops below
 1336                 'simulationEarlyTerminationTargetError'.
 1337             simulationRealTimeAnalysisMinimumTime: Minimum simulation time
 1338                 after which the real time analysis is performed.
 1339             simulationSamplerMethod: Alchemical sampling method to use:
 1340                 REPEX (Hamiltonian REPlica EXchange), SAMS (Self-Adjusted
 1341                 Mixture Sampling), or Independent (Independently sampled lambda
 1342                 windows).
 1343             simulationSamsFlatnessCriteria:Method for assessing when to switch
 1344                 to asymptomatically optimal scheme for SAMS.
 1345             simulationSamsGamma0: Initial weight adaptation rate for SAMS.
 1346             simulationTimePerIteration: Simulation time between each MCMC move
 1347                attempt 
 1348             
 1349             Solvation settings:
 1350             
 1351             Solvation parameters for the system, including the solvent model and
 1352             the solvent padding.
 1353             
 1354             solvationBoxShape: Shape of the periodic solvent box to create.
 1355             solvationBoxSize: Lengths of the unit cell for a solvent box.
 1356             solvationSolventModel: Forcefield water model to use during
 1357                 solvation and defining the model properties.
 1358             solvationSolventPadding: Minimum distance from any solute bounding
 1359                 sphere to the edge of the box.
 1360             
 1361             Thermo settings:
 1362             
 1363             Thermodynamic parameters, including the temperature and the pressure
 1364             of the system.
 1365             
 1366             thermoPh: Simulation pH
 1367             thermoPressure: Simulation pressure.
 1368             thermoRedoxPotential:Simulation redox potential.
 1369             thermoTemperature: Simulation temperature. 
 1370             
 1371     --rbfeChargeCorrection <yes or no>  [default: yes]
 1372         Perform automatic charge correction for charge changing transformations
 1373         during the calculation of RBFEs. The '--rbfeChargeCorrectionParams' are
 1374         used during the automatic charge correction to override the corresponding
 1375         values in '-r, --rbfeParams'.
 1376     --rbfeChargeCorrectionParams <Name,Value,...>  [default: auto]
 1377         A comma delimited list of parameter name and value pairs to use for RBFE
 1378         protocol settings during explicit charge correction for charge changing
 1379         transformation between pair of molecules. These parameters override the
 1380         corresponding values in '-r, --rbfeParams'.
 1381         
 1382         The default parameter values for charge changing transformations are based
 1383         on the industry benchmarking performed by OpenFE.
 1384         
 1385         The supported parameter names along with their default values are
 1386         are shown below:
 1387             
 1388             alchemicalExplicitChargeCorrection, yes [ Possible values: yes or no ]
 1389             simulationProductionLength, 20 [ Units: nanosecond ]
 1390             simulationNReplicas, 22
 1391             lambdaWindows, 22
 1392             
 1393         A brief description of these parameters is available under the corresponding
 1394         parameters in the section for '-r, --rbfeParams'.
 1395     -s, --smallMolFile <SmallMolFile>
 1396         Input file containing small molecules.
 1397     --smallMolFileParams <Name,Value,...>  [default: auto]
 1398         A comma delimited list of parameter name and value pairs for reading
 1399         molecules from files. The supported parameter names for different file
 1400         formats, along with their default values, are shown below:
 1401             
 1402             SD: removeHydrogens,no,sanitize,yes,strictParsing,yes
 1403             
 1404     --solventParams <Name,Value,...>  [default: auto]
 1405         A comma delimited list of parameter name and value pairs for solvent
 1406         component. You must specify valid OpenFE values. No extensive validation
 1407         is performed. These parameters are used in conjunction with solvation*
 1408         parameters available through '--rbfeParams' to perform solvation.
 1409         
 1410         The supported parameter names along with their default values are
 1411         are shown below:
 1412             
 1413             positiveIon, Na+ [ Possible value: Li+, Na+, K+, Rb+, or Cs+ ]
 1414             negativeIon, Cl- [ Possible values: Cl-, Br-, F-, or I- ]
 1415             neutralize, yes  [ Possible values: yes or no ]
 1416             ionConcentration, 0.15  [ Units: molar ]
 1417             
 1418         A brief description of parameters is provided below:
 1419             
 1420             positiveIon, negativeion: Pair of ions used to neutralize and bring
 1421                 the solvent to required ionic concentration.
 1422             neutralize: Neutralize the net charge on the chemical state by the
 1423                 ions in the solvent component.
 1424             ionConcentration: Ionic concentration.
 1425             
 1426     -w, --workingdir <dir>
 1427         Location of working directory which defaults to the current directory.
 1428 
 1429 Examples:
 1430     The sample protein and ligand files for tyrosine kinase 2 (Tyk2) are
 1431     distributed with MayaChemTools and are available in data directory. These
 1432     files have been taken from OpenFE distribution for example notebooks. The
 1433     AM1BCC partial charges have been calculated for the ligands in SD file to
 1434     facilitate calculations. You may review OpenFE tutorial notebooks for the
 1435     expected results.
 1436 
 1437     To calculate RBFE for a pair molecules corresponding to the fist and second
 1438     molecules in a SD file, performing 3 independent repeats of the entire MD sampling
 1439     process to calculate RBFE for a pair of molecules, each MD repeat consisting of
 1440     minimization (5,000 steps) followed by NVT and NPT equilibration (1 ns;
 1441     250,000 steps) leading to NPT production (5ns; 1,250,000 steps) using a step size
 1442     of 4 fs, writing out appropriate trajectory and PDB files for each MD repeat
 1443     in Results subdirectory under output directory, generating final results file
 1444     along with appropriate graph and image files under output directory, type:
 1445 
 1446         % OpenFECalculateRelativeBindingFreeEnergy.py -i SampleTyk2.pdb
 1447           -s SampleTyk2Ligands.sdf -o SampleTyk2LigandsRBFE
 1448 
 1449     To run the first example for calculating RBFE for a specific pair molecules
 1450     using CUDA platform on your machine to perform MD simulations and generate
 1451     various output files, type:
 1452 
 1453         % OpenFECalculateRelativeBindingFreeEnergy.py -i SampleTyk2.pdb
 1454           -s SampleTyk2Ligands.sdf -o SampleTyk2LigandsRBFE -m MoleculePairs
 1455           --moleculePairs "lig_ejm_31, lig_ejm_47"
 1456           --rbfeParams "engineComputePlatform,CUDA"
 1457 
 1458     To run the second example to see all warning messages produced by OpenFE
 1459     modules and write various output files, type;
 1460 
 1461         % OpenFECalculateRelativeBindingFreeEnergy.py -i SampleTyk2.pdb
 1462           -s SampleTyk2Ligands.sdf -o SampleTyk2LigandsRBFE -m MoleculePairs
 1463           --moleculePairs "lig_ejm_31, lig_ejm_47"
 1464           --rbfeParams "engineComputePlatform,CUDA"
 1465           --loggingLevel Warning
 1466 
 1467     To run the first example for calculating RBFE for all pairs of molecules
 1468     corresponding to edges in a molecule network using CUDA platform on your
 1469     machine to perform MD simulations and generate various output files, type:
 1470 
 1471         % OpenFECalculateRelativeBindingFreeEnergy.py -i SampleTyk2.pdb
 1472           -s SampleTyk2Ligands.sdf -o SampleTyk2LigandsRBFE -m MoleculeNetwork
 1473           --network MinimalSpanning
 1474           --rbfeParams "engineComputePlatform,CUDA"
 1475 
 1476     To run the first example for calculating RBFE for a specific pair molecules
 1477     using CUDA platform on your machine to perform MD simulations, automatically
 1478     calculate missing partial charges for molecules, and generate various output
 1479     files, type:
 1480 
 1481         % OpenFECalculateRelativeBindingFreeEnergy.py -i SampleTyk2.pdb
 1482           -s SampleTyk2LigandsNoCharges.sdf -o SampleTyk2LigandsRBFE
 1483           -m MoleculePairs --moleculePairs "lig_ejm_31, lig_ejm_47"
 1484           --rbfeParams "engineComputePlatform,CUDA"
 1485           --missingChargeMode Calculate
 1486 
 1487     To run the second example by specifying explict values for various parametres
 1488     and generate various output files, type:
 1489 
 1490         % OpenFECalculateRelativeBindingFreeEnergy.py -i SampleTyk2.pdb
 1491           -s SampleTyk2Ligands.sdf -o SampleTyk2LigandsRBFE -m MoleculePairs
 1492           --moleculePairs "lig_ejm_31, lig_ejm_47"
 1493           --loggingLevel Error
 1494           --executeDAGParams "keepShared, yes, nRetries, 2" --mapper LOMAP
 1495           --mapperParams "lomapTime, 20, lomapThreeD, yes"
 1496            --missingChargeMode Stop --rbfeParams "protocolRepeats,3,
 1497           alchemicalSoftcoreLJ, Gapsys, engineComputePlatform,CUDA,
 1498           forcefieldConstraints, HBonds, forcefieldHydrogenMass, 3.0,
 1499           forcefieldNonbondedMethod, PME, integratorTimestep, 4.0,
 1500           lambdaWindows, 11, outputCheckpointInterval, 250.0,
 1501           simulationMinimizationSteps, 5000, simulationEquilibrationLength, 1.0,
 1502           simulationProductionLength, 5.0, solvationBoxShape, cube,
 1503           solvationSolventPadding, 1.2, thermoPressure, 0.98692327,
 1504           thermoTemperature, 298.15"
 1505           --solventParams "positiveIon, Na+, negativeIon, Cl-"
 1506 
 1507 Author:
 1508     Manish Sud(msud@san.rr.com)
 1509 
 1510 See also:
 1511    OpenFECalculateAbsoluteBindingFreeEnergy.py,
 1512    OpenFECalculateAbsoluteHydrationFreeEnergy.py, OpenFECalculatePartialCharges.py,
 1513    OpenFECalculateRelativeHydrationFreeEnergy.py, OpenFEGenerateLigandNetwork.py
 1514 
 1515 Copyright:
 1516     Copyright (C) 2026 Manish Sud. All rights reserved.
 1517 
 1518     The functionality available in this script is implemented using OpenFE, an
 1519     open source molecuar for alchemical free energy calculations.
 1520 
 1521     This file is part of MayaChemTools.
 1522 
 1523     MayaChemTools is free software; you can redistribute it and/or modify it under
 1524     the terms of the GNU Lesser General Public License as published by the Free
 1525     Software Foundation; either version 3 of the License, or (at your option) any
 1526     later version.
 1527 
 1528 """
 1529 
 1530 if __name__ == "__main__":
 1531     main()