MayaChemTools

    1 #
    2 # File: OpenFEUtil.py
    3 # Author: Manish Sud <msud@san.rr.com>
    4 #
    5 # Copyright (C) 2026 Manish Sud. All rights reserved.
    6 #
    7 # The functionality available in this script is implemented using OpenFE, an
    8 # open source package for alchemical free energy calculations.
    9 #
   10 # This file is part of MayaChemTools.
   11 #
   12 # MayaChemTools is free software; you can redistribute it and/or modify it under
   13 # the terms of the GNU Lesser General Public License as published by the Free
   14 # Software Foundation; either version 3 of the License, or (at your option) any
   15 # later version.
   16 #
   17 # MayaChemTools is distributed in the hope that it will be useful, but without
   18 # any warranty; without even the implied warranty of merchantability of fitness
   19 # for a particular purpose.  See the GNU Lesser General Public License for more
   20 # details.
   21 #
   22 # You should have received a copy of the GNU Lesser General Public License
   23 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
   24 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
   25 # Boston, MA, 02111-1307, USA.
   26 #
   27 
   28 from __future__ import print_function
   29 
   30 import os
   31 import re
   32 import importlib
   33 import matplotlib.pyplot as plt
   34 import json
   35 import pathlib
   36 
   37 import openfe
   38 
   39 import kartograf
   40 import openff
   41 import gufe
   42 
   43 from openfe.utils.atommapping_network_plotting import plot_atommapping_network
   44 from openfe.protocols.openmm_afe import AbsoluteSolvationProtocol
   45 
   46 import MiscUtil
   47 import RDKitUtil
   48 
   49 __all__ = [
   50     "CalculatePartialCharges",
   51     "ExecuteProtocolDAG",
   52     "ExecuteProtocolDAGsAndGatherResults",
   53     "GenerateLigandNetwork",
   54     "GetMolFromName",
   55     "GetMolNamePresentCount",
   56     "GetMissingPartialChargesMolCount",
   57     "GetPartialChargePropName",
   58     "InitializeAbsoluteBindingFreeEngeryProtocol",
   59     "InitializeAbsoluteSolvationFreeEngeryProtocol",
   60     "InitializeAtomMapper",
   61     "InitializeAtomMappers",
   62     "InitializeAtomMapperScorer",
   63     "InitializeChemicalSystem",
   64     "InitializeProtocolDAG",
   65     "InitializeRelativeFreeEngeryHybridTopologyProtocol",
   66     "InitializeRelativeFreeEngerySeparatedTopologyProtocol",
   67     "InitializeSolventComponent",
   68     "InitializeTransformation",
   69     "IsMolNamePresent",
   70     "IsMolNamePresentMultipleTimes",
   71     "ListOpenFESettings",
   72     "ListOpenFESettingsByGroupName",
   73     "ProcessMoleculePairs",
   74     "ProcessMoleculeNames",
   75     "ProcessOptionOpenFEChargeParameters",
   76     "ProcessOptionOpenFEAbsoluteFreeEnergyMode",
   77     "ProcessOptionOpenFEAbsoluteBindingFreeEnergyParameters",
   78     "ProcessOptionOpenFEAbsoluteHydrationFreeEnergyParameters",
   79     "ProcessOptionOpenFEMapper",
   80     "ProcessOptionOpenFEExecuteDAGParameters",
   81     "ProcessOptionOpenFEMapperParameters",
   82     "ProcessOptionOpenFEMissingChargeMode",
   83     "ProcessOptionOpenFEMoleculePairs",
   84     "ProcessOptionOpenFENetwork",
   85     "ProcessOptionOpenFENetworkParameters",
   86     "ProcessOptionOpenFERelativeFreeEnergyMode",
   87     "ProcessOptionOpenFERelativeFreeEnergyChargeCorrectionParameters",
   88     "ProcessOptionOpenFERelativeFreeEnergyParameters",
   89     "ProcessOptionOpenFERelativeFreeEnergySeparatedTopologyParameters",
   90     "ProcessOptionOpenFERelativeFreeEnergyVacuumParameters",
   91     "ProcessOptionOpenFEResultFileParameters",
   92     "ProcessOptionOpenFESolventParameters",
   93     "ProcessRadialCentralLigandName",
   94     "ReadAndValidateMolecules",
   95     "ReadPDBFile",
   96     "SetupAbsoluteBindingFreeEnergySettings",
   97     "SetupAbsoluteHydrationFreeEnergySettings",
   98     "SetupRelativeFreeEnergySettings",
   99     "SetupRelativeFreeEnergySeparatedTopologySettings",
  100     "SuggestAtomMappingsForMoleculePairs",
  101     "UpdateRelativeFreeEnergySettingsForChargeCorrection",
  102     "UpdateRelativeFreeEnergySettingsForVacuum",
  103     "WriteLigandNetworkGraphMLFile",
  104     "WriteLigandNetworkImageFile",
  105     "WriteMappingImageFile",
  106     "WriteProtocolDAGResultFile",
  107 ]
  108 
  109 
  110 def InitializeRelativeFreeEngeryHybridTopologyProtocol(RBFESettings):
  111     """Initialize relative free energy hybrid topology protocol.
  112 
  113     Arguments:
  114         RBFESettings (object): OpenFE RelativeHybridTopologyProtocol settings
  115            object.
  116 
  117     Returns:
  118         object: OpenFE RelativeHybridTopologyProtocol object.
  119 
  120     """
  121 
  122     try:
  123         RBFEProtocol = openfe.protocols.openmm_rfe.RelativeHybridTopologyProtocol(settings=RBFESettings)
  124     except Exception as ErrMsg:
  125         MiscUtil.PrintInfo("")
  126         MiscUtil.PrintError("Failed to initialize relative hybrid topology procotol :\n%s" % ErrMsg)
  127 
  128     return RBFEProtocol
  129 
  130 
  131 def InitializeRelativeFreeEngerySeparatedTopologyProtocol(RBFESettings):
  132     """Initialize relative free energy separated topologies protocol.
  133 
  134     Arguments:
  135         RBFESettings (object): OpenFE SeparatedTopologyProtocol settings
  136            object.
  137 
  138     Returns:
  139         object: OpenFE SeparatedTopologyProtocol object.
  140 
  141     """
  142 
  143     try:
  144         from openfe.protocols.openmm_septop import SepTopProtocol
  145         RBFEProtocol = SepTopProtocol(RBFESettings)
  146     except Exception as ErrMsg:
  147         MiscUtil.PrintInfo("")
  148         MiscUtil.PrintError("Failed to initialize separated topologies procotol :\n%s" % ErrMsg)
  149 
  150     return RBFEProtocol
  151 
  152 
  153 def InitializeAbsoluteBindingFreeEngeryProtocol(ABFESettings):
  154     """Initialize absolute binding free energy protocol.
  155 
  156     Arguments:
  157         ABFESettings (object): OpenFE AbsoluteBindindProtocol settings
  158            object.
  159 
  160     Returns:
  161         object: OpenFE AbsoluteBindingProtocol object.
  162 
  163     """
  164 
  165     try:
  166         from openfe.protocols.openmm_afe import AbsoluteBindingProtocol
  167 
  168         ABFEProtocol = AbsoluteBindingProtocol(settings=ABFESettings)
  169     except Exception as ErrMsg:
  170         MiscUtil.PrintInfo("")
  171         MiscUtil.PrintError("Failed to initialize absolute binding procotol :\n%s" % ErrMsg)
  172 
  173     return ABFEProtocol
  174 
  175 
  176 def InitializeAbsoluteSolvationFreeEngeryProtocol(AHFESettings):
  177     """Initialize absolute solvation free energy protocol.
  178 
  179     Arguments:
  180         AHFESettings (object): OpenFE AbsoluteSolvationProtocol settings
  181            object.
  182 
  183     Returns:
  184         object: OpenFE AbsoluteSolvationProtocol object.
  185 
  186     """
  187 
  188     try:
  189         AHFEProtocol = AbsoluteSolvationProtocol(settings=AHFESettings)
  190     except Exception as ErrMsg:
  191         MiscUtil.PrintInfo("")
  192         MiscUtil.PrintError("Failed to initialize absolute solvation procotol :\n%s" % ErrMsg)
  193 
  194     return AHFEProtocol
  195 
  196 
  197 def InitializeSolventComponent(SolventParamsInfo):
  198     """Initialize a solvent component.
  199 
  200     The SolventParamsInfo parameter is a dictionary of name and value pairs for
  201     network parameters and may be generated by calling the function named
  202     ProcessOptionOpenFESolventParameters().
  203 
  204     Arguments:
  205         SolventParamsInfo (dict):  Parameter name and value pairs.
  206 
  207     Returns:
  208         object: OpenFE SolventComponent object.
  209 
  210     """
  211 
  212     try:
  213         SolventComponent = openfe.SolventComponent(
  214             positive_ion=SolventParamsInfo["PositiveIon"],
  215             negative_ion=SolventParamsInfo["NegativeIon"],
  216             neutralize=SolventParamsInfo["Neutralize"],
  217             ion_concentration=SolventParamsInfo["IonConcentration"],
  218         )
  219     except Exception as ErrMsg:
  220         MiscUtil.PrintInfo("")
  221         MiscUtil.PrintError("Failed to initialize solvent component :\n%s" % ErrMsg)
  222 
  223     return SolventComponent
  224 
  225 
  226 def InitializeChemicalSystem(SmallMol=None, MacroMol=None, Solvent=None, Name=""):
  227     """Initialize a chemical system.
  228 
  229     A valid value must be specified for at least one part of the system.
  230 
  231     Arguments:
  232         SmallMol (object): OpenFE SMC object.
  233         MacroMol (object): OpenFE PDB object.
  234         Solvent (object): OpenFE solvent component object.
  235         Name (str): Chemical system name.
  236 
  237     Returns:
  238         object: OpenFE ChemicalSystem object.
  239 
  240     """
  241 
  242     SystemComponents = {}
  243     if SmallMol is not None:
  244         SystemComponents["ligand"] = SmallMol
  245     if MacroMol is not None:
  246         SystemComponents["protein"] = MacroMol
  247     if Solvent is not None:
  248         SystemComponents["solvent"] = Solvent
  249 
  250     if len(SystemComponents.keys()) == 0:
  251         MiscUtil.PrintInfo("")
  252         MiscUtil.PrintError(
  253             "Failed to initialize chemical system. You must specify one of the following chemical components: small molecule, macro molecule, or solvent."
  254         )
  255 
  256     try:
  257         System = openfe.ChemicalSystem(components=SystemComponents, name=Name)
  258     except Exception as ErrMsg:
  259         MiscUtil.PrintInfo("")
  260         MiscUtil.PrintError("Failed to initialize chemical system:\n%s" % ErrMsg)
  261 
  262     return System
  263 
  264 
  265 def InitializeTransformation(StateA, StateB, Mapping, Protocol, Name="", Validate=False):
  266     """Initialize a transformation between two chemical systems represented by
  267     StateA and StateB.
  268 
  269     Arguments:
  270         StateA (object): OpenFE ChemicalSystem object.
  271         StateB (object): OpenFE ChemicalSystem object.
  272         Mapping (object): OpenFE mapping object.
  273         Protocol (object): OpenFE protocol object.
  274         Name (str): Transformation name.
  275         Validate (bool): Validate inputs for transformation..
  276 
  277     Returns:
  278         object: OpenFE Transformation object.
  279 
  280     """
  281 
  282     try:
  283         Transformation = openfe.Transformation(
  284             stateA=StateA, stateB=StateB, mapping=Mapping, protocol=Protocol, name=Name, validate=Validate
  285         )
  286     except Exception as ErrMsg:
  287         MiscUtil.PrintInfo("")
  288         MiscUtil.PrintError("Failed to initialize transformation:\n%s" % ErrMsg)
  289 
  290     return Transformation
  291 
  292 
  293 def InitializeProtocolDAG(Transformation, Name=""):
  294     """Create a protocol DAG (Directed Acyclic Graph) for a transformation
  295     to perform calculation.
  296 
  297     Arguments:
  298         Transformation (object): OpenFE Transformation object.
  299         Name (str): DAG name.
  300 
  301     Returns:
  302         object: OpenFE DAG object.
  303 
  304     """
  305 
  306     try:
  307         ProtocolDAG = Transformation.create(name=Name)
  308     except Exception as ErrMsg:
  309         MiscUtil.PrintInfo("")
  310         MiscUtil.PrintError("Failed to initialize protocol DAG:\n%s" % ErrMsg)
  311 
  312     return ProtocolDAG
  313 
  314 
  315 def ExecuteProtocolDAGsAndGatherResults(
  316     MolTransformations,
  317     MolProtocolDAGs,
  318     SharedOutDirPath,
  319     ScratchOutDirPath,
  320     KeepShared=True,
  321     KeepScratch=False,
  322     NRetries=0,
  323     WriteResults=True,
  324 ):
  325     """Execute protocol DAG and gather results.
  326 
  327     Arguments:
  328         MolTransformations (List): List of OpenFE transformation objects.
  329         MolProtocolDAGs (List): List of OpenFE DAG objects.
  330         SharedOutDirPath (str): Shared results directory path.
  331         ScratchOutDirPath (str): Scratch results directory path.
  332         KeepShared (bool): Keep shared directory.
  333         KeepScratch (bool): Keep scratch directory.
  334         NRetries (int): Number of times to attempt the execution. A value
  335             0 implies only 1 try.
  336         WriteResults (bool): Write results to a JSON file.
  337 
  338     Returns:
  339         list: List of OpenFE DAG result objects.
  340 
  341     """
  342 
  343     MiscUtil.PrintInfo("\nExecuting protocol DAGs...")
  344 
  345     MolProtocolResults = []
  346 
  347     DAGCount = len(MolProtocolDAGs)
  348     DAGFailedCount = 0
  349     for Index in range(0, len(MolProtocolDAGs)):
  350         DAGNum = Index + 1
  351         ProtocolResult = _ExecuteProtocolDAGAndGatherResult(
  352             MolTransformations[Index],
  353             MolProtocolDAGs[Index],
  354             DAGNum,
  355             DAGCount,
  356             SharedOutDirPath,
  357             ScratchOutDirPath,
  358             KeepShared=KeepShared,
  359             KeepScratch=KeepScratch,
  360             NRetries=NRetries,
  361             WriteResults=WriteResults,
  362         )
  363 
  364         MolProtocolResults.append(ProtocolResult)
  365 
  366         if ProtocolResult is None:
  367             DAGFailedCount += 1
  368 
  369     MiscUtil.PrintInfo("\nTotal number of protocol DAGs: %s" % DAGCount)
  370     MiscUtil.PrintInfo("Number of protocol DAGs successfully executed: %s" % (DAGCount - DAGFailedCount))
  371     MiscUtil.PrintInfo("Number of protocol DAGs failed during execution %s" % DAGFailedCount)
  372 
  373     return MolProtocolResults
  374 
  375 
  376 def _ExecuteProtocolDAGAndGatherResult(
  377     Transformation,
  378     ProtocolDAG,
  379     DAGNum,
  380     DAGCount,
  381     ResultSharedOutDirPath,
  382     ResultScratchOutDirPath,
  383     KeepShared=True,
  384     KeepScratch=False,
  385     NRetries=0,
  386     WriteResults=True,
  387 ):
  388     """Execute DAG and gather result."""
  389 
  390     SharedOutDirPath = pathlib.Path(ResultSharedOutDirPath)
  391     ScratchOutDirPath = pathlib.Path(ResultScratchOutDirPath)
  392 
  393     (WallClockTime, ProcessorTime) = MiscUtil.GetWallClockAndProcessorTime()
  394     MiscUtil.PrintInfo("\nExecuting protocol DAG %s (%s of %s)..." % (ProtocolDAG.name, DAGNum, DAGCount))
  395 
  396     ProtocolDAGResult = ExecuteProtocolDAG(
  397         ProtocolDAG,
  398         SharedOutDirPath,
  399         ScratchOutDirPath,
  400         KeepShared=KeepShared,
  401         KeepScratch=KeepScratch,
  402         NRetries=NRetries,
  403     )
  404 
  405     ProtocolResult = None
  406     if ProtocolDAGResult is not None:
  407         # Gather results...
  408         MiscUtil.PrintInfo("\nGathering results...")
  409         ProtocolResult = Transformation.protocol.gather([ProtocolDAGResult])
  410 
  411         if WriteResults:
  412             MiscUtil.PrintInfo("Writing result file...")
  413             ResultsFilePath = os.path.join(ResultSharedOutDirPath, "%s_Results.json" % ProtocolDAG.name)
  414             WriteProtocolDAGResultFile(ProtocolDAGResult, ProtocolResult, ResultsFilePath)
  415 
  416     MiscUtil.PrintInfo("Completion time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime))
  417 
  418     return ProtocolResult
  419 
  420 
  421 def ExecuteProtocolDAG(
  422     ProtocolDAG, SharedOutDirPath, ScratchOutDirPath, KeepShared=True, KeepScratch=False, NRetries=0
  423 ):
  424     """Execute protocol DAG to perform simulations for calculating FE.
  425 
  426     Arguments:
  427         ProtocolDAG (object): OpenFE protocol DAG object.
  428         SharedOutDirPath (object): Pathlib path object.
  429         ScratchOutDirPath (object): Pathlib path object.
  430         KeepShared (bool): Keep shared directory.
  431         KeepScratch (bool): Keep scratch directory.
  432         NRetries (int): Number of times to attempt the execution. A value
  433             0 implies only 1 try.
  434 
  435     Returns:
  436         object: OpenFE DAG result object.
  437 
  438     """
  439 
  440     ProtocolDAGResult = None
  441     try:
  442         ProtocolDAGResult = openfe.execute_DAG(
  443             ProtocolDAG,
  444             shared_basedir=SharedOutDirPath,
  445             scratch_basedir=ScratchOutDirPath,
  446             keep_shared=KeepShared,
  447             keep_scratch=KeepScratch,
  448             raise_error=True,
  449             n_retries=NRetries,
  450         )
  451     except Exception as ErrMsg:
  452         ProtocolDAGResult = None
  453         MiscUtil.PrintInfo("")
  454         MiscUtil.PrintInfo("Failed to execute DAG:\n%s\n" % (ErrMsg))
  455 
  456     if ProtocolDAGResult is not None:
  457         if not ProtocolDAGResult.ok():
  458             ProtocolDAGResult = None
  459             MiscUtil.PrintInfo("")
  460             MiscUtil.PrintInfo("Failed to execute DAG: Result not ok...\n")
  461 
  462     return ProtocolDAGResult
  463 
  464 
  465 def WriteProtocolDAGResultFile(ProtocolDAGResult, ProtocolResult, ResultsFilePath):
  466     """Write DAG results to a JSON file.
  467 
  468     The file format and contents are based on the OpenFECLI code in quickrun.py.
  469 
  470     Arguments:
  471         ProtocolDAGResult (object): OpenFE DAG result object.
  472         ProtocolResult (object): OpenFE protocol result object.
  473         ResultsFilePath (str): File path.
  474 
  475     Returns:
  476         None
  477 
  478     """
  479 
  480     # Setup results...
  481     if ProtocolDAGResult is None or ProtocolResult is None:
  482         ResultsMap = {"estimate": "NA", "uncertainty": "NA", "protocol_result": "NA", "unit_results": "NA"}
  483     else:
  484         Estimate = ProtocolResult.get_estimate()
  485         Uncertainty = ProtocolResult.get_uncertainty()
  486         ResultsMap = {
  487             "estimate": Estimate,
  488             "uncertainty": Uncertainty,
  489             "protocol_result": ProtocolResult.to_dict(),
  490             "unit_results": {Unit.key: Unit.to_keyed_dict() for Unit in ProtocolDAGResult.protocol_unit_results},
  491         }
  492 
  493     # Write out results file...
  494     with open(ResultsFilePath, mode="w") as OutFH:
  495         json.dump(ResultsMap, OutFH, cls=gufe.tokenization.JSON_HANDLER.encoder)
  496 
  497 
  498 def GenerateLigandNetwork(Mols, NetworkName, NetworkParamsInfo, Mappers, MapperScorer):
  499     """Generate a ligand network for molecules using the specified atom mappers
  500     and scorer. You may specify multiple atom mappers for generating mapping
  501     between two molecules. All specified mappers are employed to identify the
  502     highest scoring edges for generating a ligand network.
  503 
  504     Possible values for network name are:  LOMAP, MinimalSpanning, or Radial.
  505 
  506     The NetworkParamsInfo parameter is a dictionary of name and value pairs for
  507     network parameters and may be generated by calling the function named
  508     ProcessOptionOpenFENetworkParameters().
  509 
  510     Arguments:
  511         Mols (list): List of OpenFE molecule objects.
  512         NetworkName (str): Network name.
  513         NetworkParamsInfo (dict): Parameter name and value pairs.
  514         Mapper (list): List of OpenFE atom mapper objects.
  515         MapperScorer (Callable): OpenFE atom mapper scorer.
  516 
  517     Returns:
  518         object: OpenFE ligand network object.
  519 
  520     """
  521 
  522     try:
  523         if re.match("^LOMAP$", NetworkName, re.I):
  524             LigandNetwork = openfe.ligand_network_planning.generate_lomap_network(
  525                 ligands=Mols,
  526                 mappers=Mappers,
  527                 scorer=MapperScorer,
  528                 distance_cutoff=NetworkParamsInfo["LomapDistanceCutoff"],
  529                 max_path_length=NetworkParamsInfo["LomapMaxPathLength"],
  530                 actives=None,
  531                 max_dist_from_active=2,
  532                 require_cycle_covering=NetworkParamsInfo["LomapRequireCycleCovering"],
  533                 radial=False,
  534                 fast=False,
  535             )
  536         elif re.match("^MinimalSpanning$", NetworkName, re.I):
  537             LigandNetwork = openfe.ligand_network_planning.generate_minimal_spanning_network(
  538                 ligands=Mols,
  539                 mappers=Mappers,
  540                 scorer=MapperScorer,
  541                 progress=NetworkParamsInfo["MinimalSpanningProgress"],
  542             )
  543         elif re.match("^Radial$", NetworkName, re.I):
  544             CentralMolName = NetworkParamsInfo["RadialCentralLigand"]
  545             if not IsMolNamePresent(Mols, CentralMolName):
  546                 MiscUtil.PrintInfo("")
  547                 MiscUtil.PrintError(
  548                     "Failed to generate ligand network: Couldn't find molecule corresponding to central ligand %s"
  549                     % CentralMolName
  550                 )
  551 
  552             if IsMolNamePresentMultipleTimes(Mols, CentralMolName):
  553                 MiscUtil.PrintInfo("")
  554                 MiscUtil.PrintError(
  555                     "Failed to generate ligand network: Found mulpliple occurrences molecule corresponding to central ligand %s"
  556                     % CentralMolName
  557                 )
  558 
  559             CentralMol = GetMolFromName(Mols, CentralMolName)
  560             OtherMols = [Mol for Mol in Mols if Mol.name != CentralMolName]
  561 
  562             LigandNetwork = openfe.ligand_network_planning.generate_radial_network(
  563                 ligands=OtherMols, central_ligand=CentralMol, mappers=Mappers, scorer=MapperScorer
  564             )
  565         else:
  566             MiscUtil.PrintInfo("")
  567             MiscUtil.PrintError("Invalid network name: %s" % NetworkName)
  568     except Exception as ErrMsg:
  569         MiscUtil.PrintInfo("")
  570         MiscUtil.PrintError("Failed to generate ligand network:\n%s" % ErrMsg)
  571 
  572     return LigandNetwork
  573 
  574 
  575 def InitializeAtomMappers(MapperNameList, MapperParamsInfo):
  576     """Initialize atom mappers.
  577 
  578     Possible values for atom mapper names: LOMAP or Kartograf.
  579 
  580     The MapperParamsInfo parameter is a dictionary of name and value pairs for
  581     network parameters and may be generated by calling the function named
  582     ProcessOptionOpenFEMapperParameters().
  583 
  584     Arguments:
  585         MapperNameList (list):List of atom mapper names.
  586         MapperParamsInfo (dict): Parameter name and value pairs.
  587 
  588     Returns:
  589         list:  List of OpenFE atom mapper objects.
  590 
  591     """
  592 
  593     Mappers = []
  594     for MapperName in MapperNameList:
  595         Mapper = InitializeAtomMapper(MapperName, MapperParamsInfo)
  596         Mappers.append(Mapper)
  597 
  598     return Mappers
  599 
  600 
  601 def InitializeAtomMapper(MapperName, MapperParamsInfo):
  602     """Initialize an atom mapper.
  603 
  604     Possible values for atom mapper names are: LOMAP or Kartograf.
  605 
  606     The MapperParamsInfo parameter is a dictionary of name and value pairs for
  607     network parameters and may be generated by calling the function named
  608     ProcessOptionOpenFEMapperParameters().
  609 
  610     Arguments:
  611         MapperName (str): Atom mapper name.
  612         MapperParamsInfo (dict): Parameter name and value pairs.
  613 
  614     Returns:
  615         object: OpenFE Atom mapper object.
  616 
  617     """
  618 
  619     Mapper = None
  620     try:
  621         if re.match("^LOMAP$", MapperName, re.I):
  622             Mapper = openfe.setup.LomapAtomMapper(
  623                 time=MapperParamsInfo["LomapTime"],
  624                 threed=MapperParamsInfo["LomapThreeD"],
  625                 max3d=MapperParamsInfo["LomapMax3D"],
  626                 element_change=MapperParamsInfo["LomapElementChange"],
  627                 seed=MapperParamsInfo["LomapSeed"],
  628                 shift=MapperParamsInfo["LomapShift"],
  629             )
  630         elif re.match("^Kartograf$", MapperName, re.I):
  631             Mapper = kartograf.KartografAtomMapper(
  632                 atom_max_distance=MapperParamsInfo["KartografAtomMaxDistance"],
  633                 atom_map_hydrogens=MapperParamsInfo["KartografAtomMapHydrogens"],
  634                 map_hydrogens_on_hydrogens_only=MapperParamsInfo["KartografMapHydrogensOnHydrogensOnly"],
  635                 map_exact_ring_matches_only=MapperParamsInfo["KartografMapExactRingMatchesOnly"],
  636                 allow_partial_fused_rings=MapperParamsInfo["KartografAllowPartialFusedRings"],
  637             )
  638         else:
  639             MiscUtil.PrintInfo("")
  640             MiscUtil.PrinError("Invalid mapper name: %s" % MapperName)
  641     except Exception as ErrMsg:
  642         MiscUtil.PrintInfo("")
  643         MiscUtil.PrintError("Failed to initialize atom mapper:\n%s" % ErrMsg)
  644 
  645     return Mapper
  646 
  647 
  648 def InitializeAtomMapperScorer(ScorerName):
  649     """Initialize an atom mapper scorer.
  650 
  651     Possible value for scorer name is LOMAP.
  652 
  653     Arguments:
  654         ScorerName (str): Atom mapper scorer name.
  655 
  656     Returns:
  657         object: Atom mapper scorer object.
  658 
  659     """
  660 
  661     Scorer = None
  662     if re.match("^LOMAP$", ScorerName, re.I):
  663         try:
  664             Scorer = openfe.lomap_scorers.default_lomap_score
  665         except Exception as ErrMsg:
  666             MiscUtil.PrintInfo("")
  667             MiscUtil.PrintError("Failed to initialize atom mapper scorer:\n%s" % ErrMsg)
  668     else:
  669         MiscUtil.PrintInfo("")
  670         MiscUtil.PrinError("Invalid atom mapper scorer name: %s" % ScorerName)
  671 
  672     return Scorer
  673 
  674 
  675 def CalculatePartialCharges(Mols, ChargeMethod, ChargeParamsInfo):
  676     """Calculate partial atomic charges for molecules and return a set of
  677     OpenFE charges molecule objects. The calculated charges are stored as
  678     value of the molecule property named 'atom.dprop.PartialCharge'.
  679 
  680     The following methods are supported to calculate partial atomic charges:
  681     AM1BCC, M1-Mulliken, Espaloma, Gasteiger, MMFF94, or NAGL.
  682 
  683     The ChargeParamsInfo parameter is a dictionary of name and value pairs for
  684     charge parameters and may be generated by calling the function named
  685     ProcessOptionOpenFEChargeParameters().
  686 
  687     Arguments:
  688         Mols (list): List of OpenFE molecule objects.
  689         ChargeMethod (str): Charge method.
  690         ChargeParamsInfo (dict): Parameter name and value pairs.
  691 
  692     Returns:
  693         list: List of OpenFE charged molecule objects.
  694         bool: True or False.
  695 
  696     """
  697 
  698     if re.match("^(AM1BCC|Espaloma|NAGL)$", ChargeMethod, re.I):
  699         (ChargedMols, Status) = _BulkAssignPartialCharges(Mols, ChargeMethod, ChargeParamsInfo)
  700     elif re.match("(AM1-Mulliken|Gasteiger|MMFF94)", ChargeMethod, re.I):
  701         (ChargedMols, Status) = _AssignPartialCharges(Mols, ChargeMethod, ChargeParamsInfo)
  702     else:
  703         MiscUtil.PrintInfo("")
  704         MiscUtil.PrintError("Failed to calculate partial charges: Invalid charge method %s" % ChargeMethod)
  705 
  706     return (ChargedMols, Status)
  707 
  708 
  709 def _BulkAssignPartialCharges(Mols, ChargeMethod, ChargeParamsInfo):
  710     """Assign partial charges using OpenFE wrapper for assigning bulk charges."""
  711 
  712     SortChargedMols = True if ChargeParamsInfo["NumProcessors"] > 1 else False
  713     if SortChargedMols:
  714         # Multiprocessing may scramble the list of molecules. Add TmpMolNum
  715         # property for sorting molecules...
  716         Mols = _AddTmpMolNumPropertyForSorting(Mols)
  717 
  718     Status = True
  719     try:
  720         # Set generate_n_conformers to None to use existing conformer...
  721         ChargedMols = openfe.protocols.openmm_utils.charge_generation.bulk_assign_partial_charges(
  722             molecules=Mols,
  723             overwrite=True,
  724             method=ChargeMethod,
  725             toolkit_backend=ChargeParamsInfo["Toolkit"],
  726             generate_n_conformers=None,
  727             nagl_model=ChargeParamsInfo["NaglModel"],
  728             processors=ChargeParamsInfo["NumProcessors"],
  729         )
  730     except Exception as ErrMsg:
  731         Status = False
  732         ChargedMols = None
  733         MiscUtil.PrintInfo("")
  734         MiscUtil.PrintWarning("Failed to bulk assign partial charges:\n%s\n" % (ErrMsg))
  735 
  736     if Status:
  737         if SortChargedMols:
  738             ChargedMols = _SortMolsAndRemoveMolNumProperty(ChargedMols)
  739 
  740     return (ChargedMols, Status)
  741 
  742 
  743 def _AddTmpMolNumPropertyForSorting(Mols):
  744     """Add TmpMolNum property for sorting molecules."""
  745 
  746     RDKitMols = []
  747     MolNum = 0
  748     for Mol in Mols:
  749         MolNum += 1
  750         RDKitMol = openfe.SmallMoleculeComponent.to_rdkit(Mol)
  751         RDKitMol.SetProp("TmpMolNum", "%s" % MolNum)
  752 
  753         RDKitMols.append(RDKitMol)
  754 
  755     Mols = [openfe.SmallMoleculeComponent.from_rdkit(Mol) for Mol in RDKitMols]
  756 
  757     return Mols
  758 
  759 
  760 def _SortMolsAndRemoveMolNumProperty(Mols):
  761     """Sort molecules using TmpMolNum property along with clearing the property."""
  762 
  763     RDKitMols = [openfe.SmallMoleculeComponent.to_rdkit(Mol) for Mol in Mols]
  764 
  765     # Setup a TmpMolNum to RDKitMol map...
  766     TmpMolNumMap = {}
  767     for RDKitMol in RDKitMols:
  768         TmpMolNum = int(RDKitMol.GetProp("TmpMolNum"))
  769         TmpMolNumMap[TmpMolNum] = RDKitMol
  770         RDKitMol.ClearProp("TmpMolNum")
  771 
  772     # Setup a sorted RDKitMols list using TmpMolNums...
  773     SortedRDKitMols = []
  774     for TmpMolNum in sorted(TmpMolNumMap.keys()):
  775         SortedRDKitMols.append(TmpMolNumMap[TmpMolNum])
  776 
  777     # Setup sorted OpenFE mols...
  778     SortedMols = [openfe.SmallMoleculeComponent.from_rdkit(RDKitMol) for RDKitMol in SortedRDKitMols]
  779 
  780     return SortedMols
  781 
  782 
  783 def _AssignPartialCharges(Mols, ChargeMethod, ChargeParamsInfo):
  784     """Assign partial charges using OpenFF method."""
  785 
  786     import tqdm
  787 
  788     ToolkitRegistry = None
  789     Toolkit = ChargeParamsInfo["Toolkit"]
  790     if re.match("^RDKit$", Toolkit, re.I):
  791         ToolkitRegistry = openff.toolkit.ToolkitRegistry([openff.toolkit.RDKitToolkitWrapper])
  792     elif re.match("^AmberTools$", Toolkit, re.I):
  793         ToolkitRegistry = openff.toolkit.ToolkitRegistry([openff.toolkit.AmberToolsToolkitWrapper])
  794 
  795     PartialChargeMethod = ChargeMethod.lower()
  796     StrictNCconformers = False
  797     NormalizePartialCharges = True
  798 
  799     UseConformerStatus = ChargeParamsInfo["UseConformer"]
  800 
  801     # Transform OpenFE molecule to OpenFF molecule...
  802     OpenFFMols = [openfe.SmallMoleculeComponent.to_openff(Mol) for Mol in Mols]
  803 
  804     Status = True
  805     ChargedMols = []
  806     (MolCount, CalcFailedCount) = [0] * 2
  807     for Mol in tqdm.tqdm(OpenFFMols, desc="Calculating charges", ncols=80, total=len(OpenFFMols)):
  808         MolCount += 1
  809 
  810         Conformers = None
  811         if UseConformerStatus and Mol.n_conformers > 0:
  812             Conformers = Mol.conformers
  813 
  814         Status = True
  815         try:
  816             if ToolkitRegistry is None:
  817                 Mol.assign_partial_charges(
  818                     partial_charge_method=PartialChargeMethod,
  819                     strict_n_conformers=StrictNCconformers,
  820                     use_conformers=Conformers,
  821                     normalize_partial_charges=NormalizePartialCharges,
  822                 )
  823             else:
  824                 Mol.assign_partial_charges(
  825                     partial_charge_method=PartialChargeMethod,
  826                     strict_n_conformers=StrictNCconformers,
  827                     use_conformers=Conformers,
  828                     normalize_partial_charges=NormalizePartialCharges,
  829                     toolkit_registry=ToolkitRegistry,
  830                 )
  831         except Exception as ErrMsg:
  832             Status = False
  833             CalcFailedCount += 1
  834             MiscUtil.PrintInfo("")
  835             MiscUtil.PrintWarning("Failed to calculate partial charges for molecule %s:\n%s\n" % (Mol.name, ErrMsg))
  836             continue
  837 
  838         # Track charges molecules...
  839         ChargedMols.append(openfe.SmallMoleculeComponent.from_openff(Mol))
  840 
  841     if len(ChargedMols) == 0:
  842         ChargedMols = None
  843 
  844     MiscUtil.PrintInfo("\nNumber of valid molecules: %d" % MolCount)
  845     MiscUtil.PrintInfo("Number of molecules failed during the the calculation of partial charges: %d" % CalcFailedCount)
  846 
  847     return (ChargedMols, Status)
  848 
  849 
  850 def ListOpenFESettings(Settings):
  851     """List setting retrieved from a protocol settings object.
  852 
  853     Arguments:
  854         Settings (object): OpenFE protocol settings object.
  855 
  856     Returns:
  857         None
  858 
  859     """
  860 
  861     try:
  862         if hasattr(Settings, "model_dump"):
  863             SettingDict = Settings.model_dump()
  864         else:
  865             SettingDict = Settings.dict()
  866     except Exception as ErrMsg:
  867         MiscUtil.PrintInfo("")
  868         MiscUtil.PrintError("Failed to list settings\n%s" % ErrMsg)
  869 
  870     for SettingName in sorted(SettingDict.keys()):
  871         SettingValue = SettingDict[SettingName]
  872         if isinstance(SettingValue, dict):
  873             MiscUtil.PrintInfo("\n%s:" % (SettingName))
  874             for Name in sorted(SettingValue):
  875                 Value = SettingValue[Name]
  876                 MiscUtil.PrintInfo("    %s:%s" % (Name, Value))
  877         else:
  878             MiscUtil.PrintInfo("\n%s:%s" % (SettingName, SettingValue))
  879             continue
  880 
  881 
  882 def ListOpenFESettingsByGroupName(Settings, SettingName):
  883     """List setting retrieved from a protocol settings object for a specified
  884     settings group name.
  885 
  886     Arguments:
  887         Settings (object): OpenFE protocol settings object.
  888 
  889     Returns:
  890         None
  891 
  892     """
  893 
  894     try:
  895         if hasattr(Settings, "model_dump"):
  896             SettingDict = Settings.model_dump()
  897         else:
  898             SettingDict = Settings.dict()
  899     except Exception as ErrMsg:
  900         MiscUtil.PrintInfo("")
  901         MiscUtil.PrintError("Failed to list settings\n%s" % ErrMsg)
  902 
  903     if SettingName in SettingDict:
  904         SettingValue = SettingDict[SettingName]
  905     else:
  906         MiscUtil.PrintWarning("No settings available for group name: %s" % SettingName)
  907         return
  908 
  909     if isinstance(SettingValue, dict):
  910         MiscUtil.PrintInfo("\n%s:" % (SettingName))
  911         for Name in sorted(SettingValue):
  912             Value = SettingValue[Name]
  913             MiscUtil.PrintInfo("    %s:%s" % (Name, Value))
  914     else:
  915         MiscUtil.PrintInfo("\n%s:%s" % (SettingName, SettingValue))
  916 
  917 
  918 def GetMolFromName(Mols, MolName):
  919     """Get the first molecule whose name matches the specified molecule name
  920     from a list of molecules.
  921 
  922     Arguments:
  923         Mols (list): List of OpenFE molecule objects.
  924         MolName (str): Molecule name
  925 
  926     Returns:
  927         object or None: Open FE molecule object.
  928 
  929     """
  930 
  931     MatchedMol = None
  932     for Mol in Mols:
  933         if Mol.name == MolName:
  934             MatchedMol = Mol
  935             break
  936 
  937     return MatchedMol
  938 
  939 
  940 def IsMolNamePresent(Mols, MolName):
  941     """Check for the presence of a molecule name in a list of molecules.
  942 
  943     Arguments:
  944         Mols (list): List OpenFE molecule objects.
  945         MolName (str): Molecule name
  946 
  947     Returns:
  948         bool: True or False.
  949 
  950     """
  951 
  952     Status = False
  953     for Mol in Mols:
  954         if Mol.name == MolName:
  955             Status = True
  956             break
  957 
  958     return Status
  959 
  960 
  961 def IsMolNamePresentMultipleTimes(Mols, MolName):
  962     """Check for the presence of a molecule name in a list of molecules.
  963 
  964     Arguments:
  965         Mols (list): List OpenFE molecule objects.
  966         MolName (str): Molecule name
  967 
  968     Returns:
  969         bool: True or False.
  970 
  971     """
  972 
  973     MatchedMolCount = GetMolNamePresentCount(Mols, MolName)
  974 
  975     return True if MatchedMolCount > 1 else False
  976 
  977 
  978 def GetMolNamePresentCount(Mols, MolName):
  979     """Get count of molecule name present in a list of molecules.
  980 
  981     Arguments:
  982         Mols (list): List OpenFE molecule objects.
  983         MolName (str): Molecule name
  984 
  985     Returns:
  986         int: Molecule name present count.
  987 
  988     """
  989 
  990     MatchedMolCount = 0
  991     for Mol in Mols:
  992         if Mol.name == MolName:
  993             MatchedMolCount += 1
  994 
  995     return MatchedMolCount
  996 
  997 
  998 def GetMissingPartialChargesMolCount(Mols):
  999     """Get count of molecules with missing partial atomic charges.
 1000 
 1001     The absence of molecule property name, atom.dprop.PartialCharge,
 1002     implies missing charges for the molecule.
 1003 
 1004     Arguments:
 1005         Mols (list): List OpenFE molecule objects.
 1006 
 1007     Returns:
 1008         int: Molecule count with missing partial charges.
 1009 
 1010     """
 1011 
 1012     PropName = GetPartialChargePropName()
 1013     MolCount = 0
 1014     for Mol in Mols:
 1015         RDKitMol = Mol.to_rdkit()
 1016         if not RDKitMol.HasProp(PropName):
 1017             MolCount += 1
 1018 
 1019     return MolCount
 1020 
 1021 
 1022 def GetPartialChargePropName():
 1023     """Get partial atomic charge property name used for associating partial
 1024     charges to a molecule.
 1025 
 1026     Arguments:
 1027         None
 1028 
 1029     Returns:
 1030         str: Propery name 'atom.dprop.PartialCharge'
 1031 
 1032     """
 1033 
 1034     PropName = "atom.dprop.PartialCharge"
 1035 
 1036     return PropName
 1037 
 1038 
 1039 def WriteLigandNetworkGraphMLFile(LigandNetwork, GraphMLOutfile):
 1040     """Write ligand network to a GraphML file.
 1041 
 1042     Arguments:
 1043         LigandNetwork (object): OpenFE ligand network object.
 1044         GraphMLOutfile (str): GraphML file path.
 1045 
 1046     Returns:
 1047         None
 1048 
 1049     """
 1050 
 1051     with open(GraphMLOutfile, "w") as Writer:
 1052         Writer.write(LigandNetwork.to_graphml())
 1053 
 1054 
 1055 def WriteLigandNetworkImageFile(LigandNetwork, ImageOutfile):
 1056     """Write ligand network to an image file.
 1057 
 1058     You must specify a valid format supported by Python module Matplotlib.
 1059     For example: PNG (.png), SVG (.svg), PDF (.pdf), etc.
 1060 
 1061     Arguments:
 1062         LigandNetwork (object): OpenFE ligand network object.
 1063         ImageOutfile (str): Image file path.
 1064 
 1065     Returns:
 1066         None
 1067 
 1068     """
 1069 
 1070     plt.figure()
 1071 
 1072     plot_atommapping_network(LigandNetwork)
 1073     plt.savefig(ImageOutfile)
 1074 
 1075     plt.close()
 1076 
 1077 
 1078 def WriteMappingImageFile(Mapping, ImageOutfile):
 1079     """Write mapping to an image file.
 1080 
 1081     You must specify PNG (.png) format for the image file.
 1082 
 1083     Arguments:
 1084         Mapping (object): OpenFE mapping object.
 1085         ImageOutfile (str):  Image file path.
 1086 
 1087     Returns:
 1088         None
 1089 
 1090     """
 1091 
 1092     Mapping.draw_to_file(ImageOutfile)
 1093 
 1094 
 1095 def ReadPDBFile(PDBFile, Name=""):
 1096     """Read molecule from a PDB file.
 1097 
 1098     The supported PDB file formats are: PDB(.pdb) and CIF (.cif)
 1099 
 1100     Arguments:
 1101         PDBFile (str): PDB file path.
 1102         Name (str): Name of macromolecule.
 1103 
 1104     Returns:
 1105         object: OpenFE PDB object.
 1106 
 1107     """
 1108 
 1109     FileDir, FileName, FileExt = MiscUtil.ParseFileName(PDBFile)
 1110     if re.match("^pdb$", FileExt, re.I):
 1111         PDBHandle = openfe.ProteinComponent.from_pdb_file(PDBFile, name=Name)
 1112     elif re.match("^cif$", FileExt, re.I):
 1113         PDBHandle = openfe.ProteinComponent.from_pdbx_file(PDBFile, name=Name)
 1114     else:
 1115         MiscUtil.PrintError("Failed to read PDB file. Invalid PDB file format %s...\n" % PDBFile)
 1116 
 1117     return PDBHandle
 1118 
 1119 
 1120 def ReadAndValidateMolecules(FileName, **KeyWordArgs):
 1121     """Read molecules from an input file, validate all molecule objects, and return
 1122     a list of valid OpenFE SmallMoleculeComponent objects along with the count of
 1123     valid and non-valid molecule objects.
 1124 
 1125     Arguments:
 1126         FileName (str): Name of a file with complete path.
 1127         **KeyWordArgs (dict) : Parameter name and value pairs for reading
 1128         and processing molecules.
 1129 
 1130     Returns:
 1131         list or None: List of valid OpenFE molecule objects.
 1132         int : Number of total molecules in input file.
 1133         int : Number of valid molecules in input file.
 1134 
 1135     Notes:
 1136         The file extension is used to determine type of the file and set up an appropriate
 1137         file reader.
 1138 
 1139     """
 1140 
 1141     # Setup a molecule reader...
 1142     RDKitMols = RDKitUtil.ReadMolecules(FileName, **KeyWordArgs)
 1143 
 1144     OpenFEMols = []
 1145     (MolCount, ValidMolCount) = [0] * 2
 1146     for RDKitMol in RDKitMols:
 1147         MolCount += 1
 1148 
 1149         if not _CheckAndValidateMolecule(RDKitMol, MolCount):
 1150             continue
 1151 
 1152         ValidMolCount += 1
 1153 
 1154         # Setup OpenFE molecule...
 1155         OpenFEMols.append(openfe.SmallMoleculeComponent.from_rdkit(RDKitMol))
 1156 
 1157     return (OpenFEMols, MolCount, ValidMolCount)
 1158 
 1159 
 1160 def _CheckAndValidateMolecule(Mol, MolCount=None):
 1161     """Check and validate RDKit molecule for OpenFE calculations."""
 1162 
 1163     if Mol is None:
 1164         return False
 1165 
 1166     # Update empty molname...
 1167     MolName = Mol.GetProp("_Name")
 1168     if MiscUtil.IsEmpty(MolName):
 1169         MolName = RDKitUtil.GetMolName(Mol, MolCount)
 1170         Mol.SetProp("_Name", MolName)
 1171 
 1172     # Check for empty molecule...
 1173     if RDKitUtil.IsMolEmpty(Mol):
 1174         MiscUtil.PrintWarning("Ignoring empty molecule: %s\n" % MolName)
 1175         return False
 1176 
 1177     # Check for invalid element symbol....
 1178     if not RDKitUtil.ValidateElementSymbols(RDKitUtil.GetAtomSymbols(Mol)):
 1179         MiscUtil.PrintWarning("Ignoring molecule containing invalid element symbols: %s\n" % MolName)
 1180         return False
 1181 
 1182     # Check for 3D flag...
 1183     if not Mol.GetConformer().Is3D():
 1184         MiscUtil.PrintWarning("3D tag is not set for molecule: %s\n" % MolName)
 1185 
 1186     # Check for missing hydrogens...
 1187     if RDKitUtil.AreHydrogensMissingInMolecule(Mol):
 1188         MiscUtil.PrintWarning("Missing hydrogens in molecule: %s\n" % MolName)
 1189 
 1190     return True
 1191 
 1192 
 1193 def ProcessRadialCentralLigandName(Mols, RadialCentralMolName):
 1194     """Check for the presence of the central ligand name, used for generating
 1195     a radial ligand network, in a list of  molecules and make sure it occurs only
 1196     once in the list.
 1197 
 1198     Arguments:
 1199         Mols (list): List of OpenFE molecule objects.
 1200         CentralMoleculeName (str): Molecule name.
 1201 
 1202     Returns:
 1203         Object or none: OpenFE molecule object or None.
 1204 
 1205     """
 1206 
 1207     if RadialCentralMolName is None:
 1208         return None
 1209 
 1210     MiscUtil.PrintInfo("\nProcessing central ligand name for radial network (%s)..." % RadialCentralMolName)
 1211 
 1212     MolCount = GetMolNamePresentCount(Mols, RadialCentralMolName)
 1213     if MolCount == 0:
 1214         MiscUtil.PrintError(
 1215             'The value specified, %s, for parameter name, radialCentralLigand, using option "-n, --networkParams" is not valid. The specified molecule name is not present in the small molecule input file.'
 1216             % (RadialCentralMolName)
 1217         )
 1218     if MolCount > 1:
 1219         MiscUtil.PrintError(
 1220             'The value specified, %s, for parameter name, radialCentralLigand, using option "-n, --networkParams" is not valid. The specified molecule name is present multiple times in the small molecule input file.'
 1221             % (RadialCentralMolName)
 1222         )
 1223 
 1224     Mol = GetMolFromName(Mols, RadialCentralMolName)
 1225 
 1226     return Mol
 1227 
 1228 
 1229 def SuggestAtomMappingsForMoleculePairs(MoleculePairs, Mappers, MapperScorer):
 1230     """Suggest atom mapping between a pair of molecules using specified mappers
 1231     and a scorer.
 1232 
 1233     You may specify multiple mappers for generating mapping between pair of
 1234     molecules. All specified mappers are employed to identify the highest scoring
 1235     mapping between a pair of molecules.
 1236 
 1237     Arguments:
 1238         Mols (list): List pf OpenFE molecule objects.
 1239         Mappers (list): List of OpenFE mapper objects.
 1240         MapperScorer (object): OpenFE scorer object.
 1241 
 1242     Returns:
 1243         list: List of OpenFE mapping objects.
 1244 
 1245     """
 1246 
 1247     MolAToMolBMappings = []
 1248     for Index in range(0, len(MoleculePairs), 2):
 1249         MolA = MoleculePairs[Index]
 1250         MolB = MoleculePairs[Index + 1]
 1251 
 1252         # Setup all atom mappings...
 1253         Mappings = []
 1254         for Mapper in Mappers:
 1255             Mapping = next(Mapper.suggest_mappings(MolA, MolB))
 1256             Mappings.append(Mapping)
 1257 
 1258         # Select the mapping with the highest score...
 1259         BestScore = 0.0
 1260         BestMapping = None
 1261         for Mapping in Mappings:
 1262             Score = MapperScorer(Mapping)
 1263             if Score > BestScore:
 1264                 BestScore = Score
 1265                 BestMapping = Mapping
 1266 
 1267         # Track the best mapping...
 1268         if BestMapping is not None:
 1269             BestMapping = BestMapping.with_annotations({"score": BestScore})
 1270             MolAToMolBMappings.append(BestMapping)
 1271 
 1272     return MolAToMolBMappings
 1273 
 1274 
 1275 def ProcessMoleculePairs(Mols, MoleculePairsList=None):
 1276     """Process molecule names, corresponding to pairs of molecules, to generate
 1277     a list of molecule objects for these names. The molecule name must be a valid
 1278     name and occur only once in the list of molecules.
 1279 
 1280     The first and the second molecule in the list of molecules is returned for an
 1281     unspecified list of molecule names.
 1282 
 1283     Arguments:
 1284         Mols (list): List of OpenFE molecule objects.
 1285         MoleculePairsList (list): List of molecule names corresponding to pairs
 1286             of molecules.
 1287 
 1288     Returns:
 1289         list: List of OpenFE molecule objects corresponding to pairs of molecule
 1290             names.
 1291 
 1292     """
 1293 
 1294     MoleculePairsMolList = None
 1295 
 1296     if MoleculePairsList is None:
 1297         # Use first two molecules...
 1298         Mol1 = Mols[0]
 1299         Mol2 = Mols[1]
 1300         MoleculePairsMolList = [Mol1, Mol2]
 1301     else:
 1302         MiscUtil.PrintInfo("\nProcessing specified molecule pairs...")
 1303 
 1304         MoleculePairsMolList = []
 1305         for MolName in MoleculePairsList:
 1306             MolCount = GetMolNamePresentCount(Mols, MolName)
 1307             if MolCount == 0:
 1308                 MiscUtil.PrintError(
 1309                     'The value specified, %s, for "--moleculePairs" is not valid. The specified molecule name is not present in the small molecule input file.'
 1310                     % (MolName)
 1311                 )
 1312             if MolCount > 1:
 1313                 MiscUtil.PrintError(
 1314                     'The value specified, %s, for option "--moleculePairs" is not valid. The specified molecule name is present multiple times in the small molecule input file.'
 1315                     % (MolName)
 1316                 )
 1317             Mol = GetMolFromName(Mols, MolName)
 1318             MoleculePairsMolList.append(Mol)
 1319 
 1320     return MoleculePairsMolList
 1321 
 1322 
 1323 def ProcessOptionOpenFEMoleculePairs(OptionName, OptionValue):
 1324     """Process molecule pairs command line option and return a list
 1325     of molecule names.
 1326 
 1327     Arguments:
 1328         OptionName (str): Command line molecule pairs option name.
 1329         OptionValue (str): Command line molecule pairs option value.
 1330 
 1331     Returns:
 1332         list or none: List of  molecule names.
 1333 
 1334     """
 1335 
 1336     MoleculePairs = OptionValue.strip()
 1337     if re.match("^auto$", MoleculePairs, re.I):
 1338         return None
 1339 
 1340     MoleculePairsWords = MoleculePairs.split(",")
 1341     if len(MoleculePairsWords) % 2:
 1342         MiscUtil.PrintError(
 1343             'The number of comma delimited values, %d, specified using "%s" option must be an even number.'
 1344             % (len(MoleculePairsWords), OptionName)
 1345         )
 1346 
 1347     MoleculePairsList = []
 1348     for Index in range(0, len(MoleculePairsWords), 2):
 1349         MoleculeName1 = MoleculePairsWords[Index].strip()
 1350         MoleculeName2 = MoleculePairsWords[Index + 1].strip()
 1351 
 1352         if MoleculeName1 == MoleculeName2:
 1353             MiscUtil.PrintError(
 1354                 'The molecule name pairs, %s and %s, specified using using "%s" option is not valid. You must specify distinct molecule names.'
 1355                 % (MoleculeName1, MoleculeName2, OptionName)
 1356             )
 1357 
 1358         MoleculePairsList.append(MoleculeName1)
 1359         MoleculePairsList.append(MoleculeName2)
 1360 
 1361     return MoleculePairsList
 1362 
 1363 
 1364 def ProcessMoleculeNames(Mols, MoleculeNamesList=None):
 1365     """Process molecule names to generate a list of molecule objects for
 1366     specified names. The molecule name must be a valid name and occur
 1367     only once in the list of molecules.
 1368 
 1369     The first molecule in the list of molecules is returned for an unspecified
 1370     list of molecule names.
 1371 
 1372     Arguments:
 1373         Mols (list): List of OpenFE molecule objects.
 1374         MoleculeNamesList (list): List of molecule names.
 1375 
 1376     Returns:
 1377         list: List of OpenFE molecule objects corresponding to molecule names.
 1378 
 1379     """
 1380 
 1381     MoleculeNamesMolList = None
 1382 
 1383     if MoleculeNamesList is None:
 1384         # Use first molecule...
 1385         MoleculeNamesMolList = Mols[0]
 1386     else:
 1387         MiscUtil.PrintInfo("\nProcessing molecule names...")
 1388 
 1389         MoleculeNamesMolList = []
 1390         for MolName in MoleculeNamesList:
 1391             MolCount = GetMolNamePresentCount(Mols, MolName)
 1392             if MolCount == 0:
 1393                 MiscUtil.PrintError(
 1394                     'The value specified, %s, for "--moleculeNames" is not valid. The specified molecule name is not present in the small molecule input file.'
 1395                     % (MolName)
 1396                 )
 1397             if MolCount > 1:
 1398                 MiscUtil.PrintError(
 1399                     'The value specified, %s, for option "--moleculeNames" is not valid. The specified molecule name is present multiple times in the small molecule input file.'
 1400                     % (MolName)
 1401                 )
 1402             Mol = GetMolFromName(Mols, MolName)
 1403             MoleculeNamesMolList.append(Mol)
 1404 
 1405     return MoleculeNamesMolList
 1406 
 1407 
 1408 def ProcessOptionOpenFEMissingChargeMode(OptionName, OptionValue):
 1409     """Process missing charge mode command line option and return a valid
 1410     canonical value.
 1411 
 1412     Valid values are: Calculate or Stop.
 1413 
 1414     Arguments:
 1415         OptionName (str): Command line missing charge mode option name.
 1416         OptionValue (str): Command line missing charge mode option value.
 1417 
 1418     Returns:
 1419         str: Canonical value for missing charge mode.
 1420 
 1421     """
 1422 
 1423     Value = OptionValue.strip()
 1424     if re.match("^Calculate$", Value, re.I):
 1425         Value = "Calculate"
 1426     elif re.match("^Stop$", Value, re.I):
 1427         Value = "Stop"
 1428     else:
 1429         MiscUtil.PrintError(
 1430             'The value specified, %s, for option "%s" is not valid. Supported values: Calculate or Stop'
 1431             % (OptionValue, OptionName)
 1432         )
 1433 
 1434     return Value
 1435 
 1436 
 1437 def ProcessOptionOpenFERelativeFreeEnergyMode(OptionName, OptionValue):
 1438     """Process relative FE mode command line option and return a valid
 1439     canonical value.
 1440 
 1441     Valid values names are: MoleculePairs or MoleculeNetwork.
 1442 
 1443     Arguments:
 1444         OptionName (str): Command line missing charge mode option name.
 1445         OptionValue (str): Command line missing charge mode option value.
 1446 
 1447     Returns:
 1448         str: Canonical value for missing charge mode.
 1449 
 1450     """
 1451 
 1452     Value = OptionValue.strip()
 1453     if re.match("^MoleculePairs$", Value, re.I):
 1454         Value = "MoleculePairs"
 1455     elif re.match("^MoleculeNetwork$", Value, re.I):
 1456         Value = "MoleculeNetwork"
 1457     else:
 1458         MiscUtil.PrintError(
 1459             'The value specified, %s, for option "%s" is not valid. Supported values: MoleculePairs or MoleculeNetwork'
 1460             % (OptionValue, OptionName)
 1461         )
 1462 
 1463     return Value
 1464 
 1465 
 1466 def ProcessOptionOpenFEAbsoluteFreeEnergyMode(OptionName, OptionValue):
 1467     """Process absolute FE mode command line option and return a valid
 1468     canonical value.
 1469 
 1470     Valid values names are: FirstMolecule, AllMolecules, or MoleculeNames.
 1471 
 1472     Arguments:
 1473         OptionName (str): Command line missing charge mode option name.
 1474         OptionValue (str): Command line missing charge mode option value.
 1475 
 1476     Returns:
 1477         str: Canonical value for missing charge mode.
 1478 
 1479     """
 1480 
 1481     Value = OptionValue.strip()
 1482     if re.match("^FirstMolecule$", Value, re.I):
 1483         Value = "FirstMolecule"
 1484     elif re.match("^AllMolecules$", Value, re.I):
 1485         Value = "AllMolecules"
 1486     elif re.match("^MoleculeNames$", Value, re.I):
 1487         Value = "MoleculeNames"
 1488     else:
 1489         MiscUtil.PrintError(
 1490             'The value specified, %s, for option "%s" is not valid. Supported values: FirstMolecule, AllMolecules, or MoleculeNames'
 1491             % (OptionValue, OptionName)
 1492         )
 1493 
 1494     return Value
 1495 
 1496 
 1497 def ProcessOptionOpenFEExecuteDAGParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo=None):
 1498     """Process parameters for protocol DAG execution and return a map
 1499     containing processed parameter names and values.
 1500 
 1501     The ParamsOptionValue is a comma delimited list of parameter name and value pairs
 1502     to setup execution of protocol DAG.
 1503 
 1504     The supported parameter names along with their default and possible
 1505     values are shown below:
 1506 
 1507         keepShared, yes  [ Possible values: yes or no ]
 1508         keepScratch, no  [ Possible values: yes or no ]
 1509         nRetries, 2  [ Possible values: >= 0. A value of 0 implies only 1 try. ]
 1510 
 1511     A brief description of parameters is provided below:
 1512 
 1513         keepShared: Keep shared directories after the execution of DAG.
 1514         keepScratch: Keep scratch directories after the execution of DAG.
 1515         nRetries: Number of times to attempt the execution.
 1516 
 1517     Arguments:
 1518         ParamsOptionName (str): Command line execute DAG parameters option name.
 1519         ParamsOptionValues (str): Comma delimited list of parameter name and value pairs.
 1520         ParamsDefaultInfo (dict): Default values to override for selected parameters.
 1521 
 1522     Returns:
 1523         dictionary: Processed parameter name and value pairs.
 1524 
 1525     """
 1526 
 1527     ParamsInfo = {"KeepShared": True, "KeepScratch": False, "NRetries": 2}
 1528 
 1529     (ValidParamNames, CanonicalParamNamesMap, ParamsOptionValue, ParamsOptionValueWords) = (
 1530         _ValidateAndCanonicalizeParameterNames(ParamsOptionName, ParamsOptionValue, ParamsInfo, ParamsDefaultInfo)
 1531     )
 1532 
 1533     if re.match("^auto$", ParamsOptionValue, re.I):
 1534         _ProcessOptionOpenFEExecuteDAGParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 1535         return ParamsInfo
 1536 
 1537     for Index in range(0, len(ParamsOptionValueWords), 2):
 1538         Name = ParamsOptionValueWords[Index].strip()
 1539         Value = ParamsOptionValueWords[Index + 1].strip()
 1540 
 1541         ParamName = CanonicalParamNamesMap[Name.lower()]
 1542         ParamValue = Value
 1543 
 1544         if re.match("^NRetries$", ParamName, re.I):
 1545             if not MiscUtil.IsInteger(Value):
 1546                 MiscUtil.PrintError(
 1547                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 1548                     % (Value, ParamName, ParamsOptionName)
 1549                 )
 1550             Value = int(Value)
 1551             if Value < 0:
 1552                 MiscUtil.PrintError(
 1553                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: >= 0\n'
 1554                     % (ParamValue, ParamName, ParamsOptionName)
 1555                 )
 1556             ParamValue = Value
 1557         elif re.match("^(KeepShared|KeepScratch)$", ParamName, re.I):
 1558             if not re.match("^(yes|no|true|false)$", Value, re.I):
 1559                 MiscUtil.PrintError(
 1560                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: yes or no'
 1561                     % (Value, Name, ParamsOptionName)
 1562                 )
 1563             ParamValue = True if re.match("^(yes|true)$", Value, re.I) else False
 1564         else:
 1565             ParamValue = Value
 1566 
 1567         # Set value...
 1568         ParamsInfo[ParamName] = ParamValue
 1569 
 1570     # Handle parameters with possible auto values...
 1571     _ProcessOptionOpenFEExecuteDAGParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 1572 
 1573     return ParamsInfo
 1574 
 1575 
 1576 def _ProcessOptionOpenFEExecuteDAGParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 1577     """Process parameters with possible auto values and perform validation."""
 1578 
 1579     # Nothing to do...
 1580     return
 1581 
 1582 
 1583 def ProcessOptionOpenFEResultFileParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo=None):
 1584     """Process parameters for result file  and return a map containing processed
 1585     parameter names and values.
 1586 
 1587     The supported parameter names along with their default and possible
 1588     values are shown below:
 1589 
 1590         precision, 4  [ Possible values: > 0 ]
 1591         delimiter, comma  [ Possible values: comma or tab ]
 1592 
 1593     Arguments:
 1594         ParamsOptionName (str): Command line result file parameters option name.
 1595         ParamsOptionValues (str): Comma delimited list of parameter name and value pairs.
 1596         ParamsDefaultInfo (dict): Default values to override for selected parameters.
 1597 
 1598     Returns:
 1599         dictionary: Processed parameter name and value pairs.
 1600 
 1601     """
 1602 
 1603     ParamsInfo = {"Precision": 4, "Delimiter": "Comma"}
 1604 
 1605     (ValidParamNames, CanonicalParamNamesMap, ParamsOptionValue, ParamsOptionValueWords) = (
 1606         _ValidateAndCanonicalizeParameterNames(ParamsOptionName, ParamsOptionValue, ParamsInfo, ParamsDefaultInfo)
 1607     )
 1608 
 1609     if re.match("^auto$", ParamsOptionValue, re.I):
 1610         _ProcessOptionOpenFEResultFileParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 1611         return ParamsInfo
 1612 
 1613     for Index in range(0, len(ParamsOptionValueWords), 2):
 1614         Name = ParamsOptionValueWords[Index].strip()
 1615         Value = ParamsOptionValueWords[Index + 1].strip()
 1616 
 1617         ParamName = CanonicalParamNamesMap[Name.lower()]
 1618         ParamValue = Value
 1619 
 1620         if re.match("^Precision$", ParamName, re.I):
 1621             if not MiscUtil.IsInteger(Value):
 1622                 MiscUtil.PrintError(
 1623                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 1624                     % (Value, ParamName, ParamsOptionName)
 1625                 )
 1626             Value = int(Value)
 1627             if Value <= 0:
 1628                 MiscUtil.PrintError(
 1629                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 1630                     % (ParamValue, ParamName, ParamsOptionName)
 1631                 )
 1632             ParamValue = Value
 1633         elif re.match("^Delimiter$", ParamName, re.I):
 1634             if not re.match("^(comma|tab)$", Value, re.I):
 1635                 MiscUtil.PrintError(
 1636                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: comma or tab'
 1637                     % (Value, Name, ParamsOptionName)
 1638                 )
 1639             ParamValue = Value
 1640         else:
 1641             ParamValue = Value
 1642 
 1643         # Set value...
 1644         ParamsInfo[ParamName] = ParamValue
 1645 
 1646     # Handle parameters with possible auto values...
 1647     _ProcessOptionOpenFEResultFileParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 1648 
 1649     return ParamsInfo
 1650 
 1651 
 1652 def _ProcessOptionOpenFEResultFileParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 1653     """Process parameters with possible auto values and perform validation."""
 1654 
 1655     ParamName = "Delimiter"
 1656     ParamValue = ParamsInfo[ParamName]
 1657 
 1658     if re.match("^Tab$", ParamValue, re.I):
 1659         FileExt = "tsv"
 1660         FileDelimiter = "\t"
 1661     elif re.match("^Comma$", ParamValue, re.I):
 1662         FileExt = "csv"
 1663         FileDelimiter = ","
 1664     else:
 1665         MiscUtil.PrintError(
 1666             'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: comma or tab'
 1667             % (ParamValue, ParamName, ParamsOptionName)
 1668         )
 1669 
 1670     ParamsInfo["Ext"] = FileExt
 1671     ParamsInfo["Delim"] = FileDelimiter
 1672 
 1673     return
 1674 
 1675 
 1676 def ProcessOptionOpenFERelativeFreeEnergyChargeCorrectionParameters(
 1677     ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo=None
 1678 ):
 1679     """Process parameters for RBFE charge correction option and return a map
 1680     containing processed parameter names and values.
 1681 
 1682     The ParamsOptionValue is a comma delimited list of parameter name and value pairs
 1683     to setup charge correction for RBFE calculations.
 1684 
 1685     The supported parameter names along with their default and possible
 1686     values are shown below:
 1687 
 1688         alchemicalExplicitChargeCorrection, yes
 1689         simulationProductionLength = 20 * unit.nanosecond
 1690         simulationNReplicas, 22
 1691         lambdaWindows, 22
 1692 
 1693     Arguments:
 1694         ParamsOptionName (str): Command line OpenFE RBFE charge correction
 1695             parameters option name.
 1696         ParamsOptionValue (str): Comma delimited list of parameter name and value pairs.
 1697         ParamsDefaultInfo (dict): Default values to override for selected parameters.
 1698 
 1699     Returns:
 1700         dictionary: Processed parameter name and value pairs.
 1701 
 1702     """
 1703 
 1704     ParamsInfo = {
 1705         "AlchemicalExplicitChargeCorrection": True,
 1706         "SimulationProductionLength": 20,
 1707         "SimulationNReplicas": 22,
 1708         "LambdaWindows": 22,
 1709     }
 1710 
 1711     (ValidParamNames, CanonicalParamNamesMap, ParamsOptionValue, ParamsOptionValueWords) = (
 1712         _ValidateAndCanonicalizeParameterNames(ParamsOptionName, ParamsOptionValue, ParamsInfo, ParamsDefaultInfo)
 1713     )
 1714 
 1715     if re.match("^auto$", ParamsOptionValue, re.I):
 1716         _ProcessOptionOpenFERelativeFreeEnergyChargeCorrectionParameters(
 1717             ParamsInfo, ParamsOptionName, ParamsOptionValue
 1718         )
 1719         return ParamsInfo
 1720 
 1721     for Index in range(0, len(ParamsOptionValueWords), 2):
 1722         Name = ParamsOptionValueWords[Index].strip()
 1723         Value = ParamsOptionValueWords[Index + 1].strip()
 1724 
 1725         ParamName = CanonicalParamNamesMap[Name.lower()]
 1726         ParamValue = Value
 1727 
 1728         if re.match("^(SimulationProductionLength|SimulationNReplicas|LambdaWindows)$", ParamName, re.I):
 1729             if not MiscUtil.IsInteger(Value):
 1730                 MiscUtil.PrintError(
 1731                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 1732                     % (Value, ParamName, ParamsOptionName)
 1733                 )
 1734             Value = int(Value)
 1735             if Value <= 0:
 1736                 MiscUtil.PrintError(
 1737                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 1738                     % (ParamValue, ParamName, ParamsOptionName)
 1739                 )
 1740             ParamValue = Value
 1741         elif re.match("^AlchemicalExplicitChargeCorrection$", ParamName, re.I):
 1742             if not re.match("^(yes|no|true|false)$", Value, re.I):
 1743                 MiscUtil.PrintError(
 1744                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: yes or no'
 1745                     % (Value, Name, ParamsOptionName)
 1746                 )
 1747             ParamValue = True if re.match("^(yes|true)$", Value, re.I) else False
 1748         else:
 1749             ParamValue = Value
 1750 
 1751         # Set value...
 1752         ParamsInfo[ParamName] = ParamValue
 1753 
 1754     # Handle parameters with possible auto values...
 1755     _ProcessOptionOpenFERelativeFreeEnergyChargeCorrectionParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 1756 
 1757     return ParamsInfo
 1758 
 1759 
 1760 def _ProcessOptionOpenFERelativeFreeEnergyChargeCorrectionParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 1761     """Process parameters with possible auto values and perform validation."""
 1762 
 1763     # Setup units for SimulationProductionLength...
 1764     ParamName = "SimulationProductionLength"
 1765     ParamValue = ParamsInfo[ParamName]
 1766     if MiscUtil.IsNumber(ParamValue):
 1767         ParamsInfo[ParamName] = ParamValue * openff.units.unit.nanosecond
 1768 
 1769 
 1770 def ProcessOptionOpenFERelativeFreeEnergyVacuumParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo=None):
 1771     """Process parameters for RBFE vacuum option and return a map containing
 1772     processed parameter names and values.
 1773 
 1774     The ParamsOptionValue is a comma delimited list of parameter name and value pairs
 1775     to setup charge correction for RBFE calculations.
 1776 
 1777     The supported parameter names along with their default and possible
 1778     values are shown below:
 1779 
 1780         forcefieldNonbondedMethod, NoCutoff [ Possible values: PME or NoCutoff ]
 1781 
 1782     Arguments:
 1783         ParamsOptionName (str): Command line OpenFE RBFE vacuum parameters
 1784             option name.
 1785         ParamsOptionValue (str): Comma delimited list of parameter name and value pairs.
 1786         ParamsDefaultInfo (dict): Default values to override for selected parameters.
 1787 
 1788     Returns:
 1789         dictionary: Processed parameter name and value pairs.
 1790 
 1791     """
 1792 
 1793     ParamsInfo = {"ForcefieldNonbondedMethod": "nocutoff"}
 1794 
 1795     (ValidParamNames, CanonicalParamNamesMap, ParamsOptionValue, ParamsOptionValueWords) = (
 1796         _ValidateAndCanonicalizeParameterNames(ParamsOptionName, ParamsOptionValue, ParamsInfo, ParamsDefaultInfo)
 1797     )
 1798 
 1799     if re.match("^auto$", ParamsOptionValue, re.I):
 1800         _ProcessOptionOpenFERelativeFreeEnergyVacuumParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 1801         return ParamsInfo
 1802 
 1803     for Index in range(0, len(ParamsOptionValueWords), 2):
 1804         Name = ParamsOptionValueWords[Index].strip()
 1805         Value = ParamsOptionValueWords[Index + 1].strip()
 1806 
 1807         ParamName = CanonicalParamNamesMap[Name.lower()]
 1808         ParamValue = Value
 1809 
 1810         if re.match("^ForcefieldNonbondedMethod$", ParamName, re.I):
 1811             if not re.match("^(PME|NoCutoff)$", Value, re.I):
 1812                 MiscUtil.PrintError(
 1813                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is may be a valid OpenFE value. Supported values: PME or NoCutoff'
 1814                     % (Value, Name, ParamsOptionName)
 1815                 )
 1816             ParamValue = Value.lower()
 1817         else:
 1818             ParamValue = Value
 1819 
 1820         # Set value...
 1821         ParamsInfo[ParamName] = ParamValue
 1822 
 1823     # Handle parameters with possible auto values...
 1824     _ProcessOptionOpenFERelativeFreeEnergyVacuumParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 1825 
 1826     return ParamsInfo
 1827 
 1828 
 1829 def _ProcessOptionOpenFERelativeFreeEnergyVacuumParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 1830     """Process parameters with possible auto values and perform validation."""
 1831 
 1832     # Nothing to do...
 1833     return
 1834 
 1835 
 1836 def ProcessOptionOpenFERelativeFreeEnergyParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo=None):
 1837     """Process parameters for RFE parameters option and return a map
 1838     containing processed parameter names and values.
 1839 
 1840     The ParamsOptionValue is a comma delimited list of parameter name and value pairs
 1841     to setup RFE calculations.
 1842 
 1843     The default values are automatically updated to match settings provided by
 1844     OpenFE module RelativeHybridTopologyProtocol.
 1845 
 1846     You must specify valid OpenFE values for these parameters. An extensive
 1847     validation is not performed.
 1848 
 1849     The supported parameter names along with their default and possible
 1850     values are shown below:
 1851 
 1852         protocolRepeats, 3
 1853 
 1854         Alchemical settings:
 1855 
 1856         alchemicalEndstateDispersionCorrection, no  [ Possible values:
 1857             yes or no ]
 1858         alchemicalExplicitChargeCorrection, no  [ Possible values:
 1859             yes or no ]
 1860         alchemicalExplicitChargeCorrectionCutoff, 0.8  [ Units: nanometer ]
 1861         alchemicalSoftcoreLJ, Gapsys [ Possible values: Gapsys or Beutler ]
 1862         alchemicalSoftcoreAlpha, 0.85
 1863         alchemicalTurnOffCoreUniqueExceptions, no  [ Possible values:
 1864             yes or no ]
 1865         alchemicalUseDispersionCorrection, no [ Possible values: yes or no ]
 1866 
 1867         Engine settings:
 1868 
 1869         engineComputePlatform, CPU  [ Possible values: CPU, CUDA, OpenCL,
 1870             or Reference ]
 1871         engineGpuDeviceIndex, None [ Possible values: 0, 0 1, etc. ]
 1872 
 1873         Forcefield settings:
 1874 
 1875         forcefieldConstraints, HBonds  [ Possible values: HBonds, ALLBonds or
 1876             HAngles  ]
 1877         forcefields, ['amber/ff14SB.xml', 'amber/tip3p_standard.xml',
 1878             'amber/tip3p_HFE_multivalent.xml', 'amber/phosaa10.xml']
 1879             [ Possible values: A space delimited list of valid names. ]
 1880         forcefieldHydrogenMass, 3.0  [ Units: amu ]
 1881         forcefieldNonbondedCutoff, 0.9  [ Units: nanometer ]
 1882         forcefieldNonbondedMethod, PME [ Possible values: PME or NoCutoff ]
 1883         forcefieldRigidWater, yes  [ Possible values: yes or no ]
 1884         forcefieldSmallMoleculeForcefield, openff-2.1.1  [ Possible value:
 1885             A valid forcefield name. ]
 1886 
 1887         Integrator settings:
 1888 
 1889         integratorBarostatFrequency, 25.0 * timestep  [ The specified value
 1890             is a multiple of integratorTimestep. ]
 1891         integratorConstraintTolerance, 1e-06
 1892         integratorLangevinCollisionRate, 1.0  [ Units: 1 / picosecond ]
 1893         integratorNRestartAttempts, 20
 1894         integratorReassignVelocities, no  [ Possible values: yes or no ]
 1895         integratorRemoveCom, no  [ Possible values: yes or no ]
 1896         integratorTimestep, 4.0 [ Units: femtosecond ]
 1897 
 1898         Lambda settings:
 1899 
 1900         lambdaFunctions, default  [ Possible values: Default, namd, or
 1901             quarters ]
 1902         lambdaWindows, 11
 1903 
 1904         Output settings:
 1905 
 1906         outputCheckpointInterval, 1.0 [ Units: nanosecond ]
 1907         outputCheckpointStorageFilename, checkpoint.chk
 1908         outputForcefieldCache, db.json
 1909         outputFilename, simulation.nc
 1910         outputIndices, not water  [ Possible value: Any valid selection. ]
 1911         outputStructure, hybrid_system.pdb
 1912         outputPositionsWriteFrequency, 100.0 [ Units: picosecond ]
 1913         outputVelocitiesWriteFrequency, None  [  Possible values: > 0;
 1914             Units: picosecond ]
 1915 
 1916         Partial charge settings:
 1917 
 1918         partialChargeNaglModel, None  [ Default: Production AM1BCC model for
 1919             NAGL; Possible value: Any valid name. ]
 1920         partialChargeNumberOfConformers, None  [ Possible value: > 0 ]
 1921         partialChargeOffToolkitBackend, AmberTools  [ Possible values:
 1922             AmberTools or RDKit ]
 1923         partialChargeMethod, AM1BCC  [ Possble values: AM1BCC, Espaloma,
 1924             or NAGL ]
 1925 
 1926         Simulation settings:
 1927 
 1928         simulationEarlyTerminationTargetError, 0.0 [ Units:
 1929             kilocalorie_per_mole ]
 1930         simulationEquilibrationLength, 1.0 [ Units: nanosecond ]
 1931         simulationMinimizationSteps, 5000
 1932         simulationNReplicas, 11
 1933         simulationProductionLength, 5.0 [ Units: nanosecond ]
 1934         simulationRealTimeAnalysisInterval, 250.0 [ Units: picosecond ]
 1935         simulationRealTimeAnalysisMinimumTime, 500.0  [ Units: picosecond ]
 1936         simulationSamplerMethod, repex  [ Possible values: repex, sams,
 1937             or independent ]
 1938         simulationSamsFlatnessCriteria, logZ-flatness  [ Possible values:
 1939             logZ-flatness, minimum-visits or histogram-flatness ]
 1940         simulationSamsGamma0, 1.0
 1941         simulationTimePerIteration, 2.5  [ Units: picosecond ]
 1942 
 1943         Solvation settings:
 1944 
 1945         solvationBoxShape, dodecahedron  [  Possible values: cube,
 1946             dodecahedron, or octahedron ]
 1947         solvationBoxSize, None  [ Possible value: A triplet of space
 1948             X Y Z values; Units: nanometer ]
 1949         solvationSolventModel, tip3p  [ Possible values: tip3p, spce, tip4pew,
 1950             or tip5p ]
 1951         solvationSolventPadding, 1.5  [ Units: nanometer ]
 1952 
 1953         Thermo settings:
 1954 
 1955         thermoPh, None  [ Possible values: > 0 ]
 1956         thermoPressure, 1.0  [ Units: bar ]
 1957         thermoRedoxPotential, None  [ Possible values: A valid float.
 1958             Units: millivolts (mV) ]
 1959         thermoTemperature, 298.15  [ Units: kelvin ]
 1960 
 1961     A brief description of parameters, taken from OpenFE documentation, is
 1962     provided below:
 1963 
 1964         protocolRepeats: Number of completely independent repeats of the
 1965             entire sampling process.
 1966 
 1967         Alchemical settings:
 1968 
 1969         Parameters controlling the creation of the hybrid topology system,
 1970         including various parameters ranging from softcore parameters to
 1971         whether or not to apply an explicit charge correction for systems
 1972         with net charge changes.
 1973 
 1974         alchemicalEndstateDispersionCorrection: Employ extra unsampled
 1975             endstate windows for long range correction.
 1976         alchemicalExplicitChargeCorrection: Explicitly account for a charge
 1977             difference during the alchemical transformation by transforming
 1978             a water to a counterion of the opposite charge of the formal
 1979             charge difference.
 1980         alchemicalExplicitChargeCorrectionCutoff: Minimum distance from the
 1981             system solutes from which an alchemical water can be chosen.
 1982         alchemicalSoftcoreLJ: Use LJ softcore function as defined by Gapsys
 1983             [ Ref 181 ] or Buetler [ Ref 182 ].
 1984         alchemicalSoftcoreAlpha: Softcore alpha parameter.
 1985             alchemicalTurnOffCoreUniqueExceptions: Turn off interactions for
 1986             new exceptions (not just 1,4s) at lambda 0 and old exceptions at
 1987             lambda 1 between unique atoms and core atoms.
 1988         alchemicalUseDispersionCorrection: Use dispersion correction in the
 1989             hybrid topology state.
 1990 
 1991         Engine settings:
 1992 
 1993         Parameters configuring the compute platform used by the OpenMM to
 1994         perform the simulation.
 1995 
 1996         engineComputePlatform: Platform to use for running OpenMM MD
 1997             calculations.
 1998         engineGpuDeviceIndex: Space delimited list of device indices to use
 1999             for running OpenMM MD calculations.
 2000 
 2001         Forcefield settings:
 2002 
 2003         Parameters to set up the force field with OpenMM Force Fields,
 2004         including the general force fields, the small molecule force field,
 2005         the nonbonded method, and the nonbonded cutoff.
 2006 
 2007         forcefieldConstraints: Constraints to use.
 2008         forcefields: List of valid forcefield paths for all components
 2009             except small molecules.
 2010         forcefieldHydrogenMass: Mass to be repartitioned to hydrogens from
 2011             neighboring heavy atoms.
 2012         forcefieldNonbondedCutoff: Cutoff for short range nonbonded
 2013             interactions.
 2014         forcefieldNonbondedMethod: Method for treating nonbonded
 2015             interactions.
 2016         forcefieldRigidWater: Use a rigid water model.
 2017         forcefieldSmallMoleculeForcefield: A valid forcefield name to use
 2018             for small molecules.
 2019 
 2020         Integrator settings
 2021 
 2022         Parameters controlling the LangevinSplittingDynamicsMove integrator
 2023         used for simulation.
 2024 
 2025         integratorBarostatFrequency: Frequency at which volume scaling
 2026             changes should be attempted.
 2027         integratorConstraintTolerance: Tolerance for constraint solver.
 2028         integratorLangevinCollisionRate: Collision frequency.
 2029         integratorNRestartAttempts: Number of attempts to restart from
 2030             Context in case there are NaNs in the energies after
 2031             integration.
 2032         integratorReassignVelocities: Reassign velocities  from the
 2033             Maxwell-Boltzmann distribution at the beginning of each
 2034             Monte Carlo move.
 2035         integratorRemoveCom: Remove the center of mass motion.
 2036         integratorTimestep: Size of the simulation timestep.
 2037 
 2038         Lambda settings:
 2039 
 2040         Lambda protocol parameters, including number of lambda windows and
 2041         lambda functions.
 2042 
 2043         lambdaFunctions: Function name to use for alchemical mutation.
 2044         lambdaWindows: Number of lambda windows to calculate.
 2045 
 2046         Output settings:
 2047 
 2048         Parameter controlling simulation output, including the frequency to
 2049         write a checkpoint file, the selection string for writing selected
 2050         coordinates, and the paths to the trajectory and output structure
 2051         files.
 2052 
 2053         outputCheckpointInterval: Frequency to write the checkpoint file.
 2054         outputCheckpointStorageFilename: Checkpoint filename.
 2055         outputForcefieldCache: Filename for caching small molecule residue
 2056             templates.
 2057         outputFilename: Trajectory filename.
 2058         outputIndices: Selection string for selecting coordinates to write.
 2059         outputStructure: Hybrid topology structure filename.
 2060         outputPositionsWriteFrequency: Frequency for writing positions to
 2061             trajectory file.
 2062         outputVelocitiesWriteFrequency: Frequency for writing velocities to
 2063             trajectory file.
 2064 
 2065         Partial charge settings:
 2066 
 2067         Parameters for automatically assigning missing partial charges to
 2068         small molecules, including the partial charge method.
 2069 
 2070         partialChargeNaglModel: Model to use for partial charge assignment.
 2071             A value of None implies the use of the latest available
 2072             production AM1BCC model.
 2073         partialChargeNumberOfConformers: Number of conformers to generate
 2074             as part of the partial charge assignment. A value of None
 2075             implies the use of the existing conformer.
 2076         partialChargeOffToolkitBackend: OpenFF toolkit registry backend to
 2077             use for calculating partial charges.
 2078         partialChargeMethod: Method to use for calculating partial charges.
 2079 
 2080         Simulation settings:
 2081 
 2082         Parameters controlling the simulation plan and the alchemical
 2083         sampler, including the number of minimization steps, lengths of
 2084         equilibration and production runs, the sampler method (e.g.
 2085         Hamiltonian REPlica EXchange (repex), and the time interval at
 2086         which to perform an analysis of the free energies.
 2087 
 2088         simulationEarlyTerminationTargetError: Target error for the real
 2089             time analysis measured in kcal/mol. Once the MBAR error of the
 2090             free energy is at or below this value, the simulation will be
 2091             considered complete. The suggested value of 0.12 has shown to
 2092             be effective in both hydration and binding free energy
 2093             benchmarks.
 2094         simulationEquilibrationLength: Length of the equilibration phase.
 2095             The specified value must be divisible by 'integratorTimestep'.
 2096         simulationMinimizationSteps: Number of minimization steps to
 2097             perform.
 2098         simulationNReplicas: Number of replicas to use.
 2099         simulationProductionLength: Length of the production phase.
 2100             The specified value must be divisible by 'integratorTimestep'.
 2101         simulationRealTimeAnalysisInterval: Time interval for performing
 2102             analysis of the free energies. At each interval, real time
 2103             analysis data will be written to a yaml file named
 2104             <outputFileName>_real_time_analysis.yaml. The current error
 2105             in the estimate will also be assessed and the simulation will
 2106             be terminated when it drops below
 2107             'simulationEarlyTerminationTargetError'.
 2108         simulationRealTimeAnalysisMinimumTime: Minimum simulation time
 2109             after which the real time analysis is performed.
 2110         simulationSamplerMethod: Alchemical sampling method to use:
 2111             REPEX (Hamiltonian REPlica EXchange), SAMS (Self-Adjusted
 2112             Mixture Sampling), or Independent (Independently sampled lambda
 2113             windows).
 2114         simulationSamsFlatnessCriteria:Method for assessing when to switch
 2115             to asymptomatically optimal scheme for SAMS.
 2116         simulationSamsGamma0: Initial weight adaptation rate for SAMS.
 2117         simulationTimePerIteration: Simulation time between each MCMC move
 2118             attempt
 2119 
 2120         Solvation settings:
 2121 
 2122         Solvation parameters for the system, including the solvent model and
 2123         the solvent padding.
 2124 
 2125         solvationBoxShape: Shape of the periodic solvent box to create.
 2126         solvationBoxSize: Lengths of the unit cell for a solvent box.
 2127         solvationSolventModel: Forcefield water model to use during
 2128             solvation and defining the model properties.
 2129         solvationSolventPadding: Minimum distance from any solute bounding
 2130             sphere to the edge of the box.
 2131 
 2132         Thermo settings:
 2133 
 2134         Thermodynamic parameters, including the temperature and the pressure
 2135         of the system.
 2136 
 2137         thermoPh: Simulation pH.
 2138         thermoPressure: Simulation pressure.
 2139         thermoRedoxPotential:Simulation redox potential.
 2140         thermoTemperature: Simulation temperature.
 2141 
 2142     Arguments:
 2143         ParamsOptionName (str): Command line OpenFE RFE parameters option name.
 2144         ParamsOptionValue (str): Comma delimited list of parameter name and value pairs.
 2145         ParamsDefaultInfo (dict): Default values to override selected parameters.
 2146 
 2147     Returns:
 2148         dictionary: Processed parameter name and value pairs.
 2149 
 2150     """
 2151     ParamsInfo = _SetupRelativeFreeEnergyDefaultParametersInfo(ParamsOptionName, ParamsOptionValue)
 2152 
 2153     (ValidParamNames, CanonicalParamNamesMap, ParamsOptionValue, ParamsOptionValueWords) = (
 2154         _ValidateAndCanonicalizeParameterNames(ParamsOptionName, ParamsOptionValue, ParamsInfo, ParamsDefaultInfo)
 2155     )
 2156 
 2157     if re.match("^auto$", ParamsOptionValue, re.I):
 2158         _ProcessOptionOpenFERelativeFreeEnergyParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 2159         return ParamsInfo
 2160 
 2161     for Index in range(0, len(ParamsOptionValueWords), 2):
 2162         Name = ParamsOptionValueWords[Index].strip()
 2163         Value = ParamsOptionValueWords[Index + 1].strip()
 2164 
 2165         ParamName = CanonicalParamNamesMap[Name.lower()]
 2166         ParamValue = Value
 2167 
 2168         if re.match(
 2169             "^(ProtocolRepeats|IntegratorNRestartAttempts|LambdaWindows|PartialChargeNumberOfConformers|SimulationMinimizationSteps|SimulationNReplicas|solvationNumbeOfSolventMolecules)$",
 2170             ParamName,
 2171             re.I,
 2172         ):
 2173             #  Int > 0
 2174             if not MiscUtil.IsInteger(Value):
 2175                 MiscUtil.PrintError(
 2176                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 2177                     % (Value, ParamName, ParamsOptionName)
 2178                 )
 2179             Value = int(Value)
 2180             if Value <= 0:
 2181                 MiscUtil.PrintError(
 2182                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 2183                     % (ParamValue, ParamName, ParamsOptionName)
 2184                 )
 2185             ParamValue = Value
 2186         elif re.match(
 2187             "^(AlchemicalSoftcoreAlpha|ForcefieldHydrogenMass|IntegratorConstraintTolerance|SimulationSamsGamma0)$",
 2188             ParamName,
 2189             re.I,
 2190         ):
 2191             # float > 0
 2192             if not MiscUtil.IsFloat(Value):
 2193                 MiscUtil.PrintError(
 2194                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2195                     % (Value, ParamName, ParamsOptionName)
 2196                 )
 2197             Value = float(Value)
 2198             if Value <= 0:
 2199                 MiscUtil.PrintError(
 2200                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 2201                     % (ParamValue, ParamName, ParamsOptionName)
 2202                 )
 2203             ParamValue = Value
 2204         elif re.match("^ThermoPh$", ParamName, re.I):
 2205             #  float > 0 or None
 2206             if re.match("^None$", Value, re.I):
 2207                 ParamValue = None
 2208             else:
 2209                 if not MiscUtil.IsFloat(Value):
 2210                     MiscUtil.PrintError(
 2211                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2212                         % (Value, ParamName, ParamsOptionName)
 2213                     )
 2214                 Value = float(Value)
 2215                 if Value <= 0:
 2216                     MiscUtil.PrintError(
 2217                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 2218                         % (ParamValue, ParamName, ParamsOptionName)
 2219                     )
 2220                 ParamValue = Value
 2221         elif re.match("^ThermoRedoxPotential$", ParamName, re.I):
 2222             if re.match("^None$", Value, re.I):
 2223                 ParamValue = None
 2224             else:
 2225                 if not MiscUtil.IsFloat(Value):
 2226                     MiscUtil.PrintError(
 2227                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2228                         % (Value, ParamName, ParamsOptionName)
 2229                     )
 2230                 Value = float(Value)
 2231                 ParamValue = Value * openff.units.unit.millivolts
 2232         elif re.match(
 2233             "^(AlchemicalEndstateDispersionCorrection|AlchemicalExplicitChargeCorrection|AlchemicalTurnOffCoreUniqueExceptions|AlchemicalUseDispersionCorrection|ForcefieldRigidWater|IntegratorReassignVelocities|IntegratorRemoveCom)$",
 2234             ParamName,
 2235             re.I,
 2236         ):
 2237             #  bool
 2238             if not re.match("^(yes|no|true|false)$", Value, re.I):
 2239                 MiscUtil.PrintError(
 2240                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: yes or no'
 2241                     % (Value, Name, ParamsOptionName)
 2242                 )
 2243             ParamValue = True if re.match("^(yes|true)$", Value, re.I) else False
 2244         elif re.match("^(AlchemicalExplicitChargeCorrectionCutoff|ForcefieldNonbondedCutoff)$", ParamName, re.I):
 2245             #  float > 0 and units nanometer
 2246             if not MiscUtil.IsFloat(Value):
 2247                 MiscUtil.PrintError(
 2248                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2249                     % (Value, ParamName, ParamsOptionName)
 2250                 )
 2251             Value = float(Value)
 2252             if Value <= 0:
 2253                 MiscUtil.PrintError(
 2254                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 2255                     % (ParamValue, ParamName, ParamsOptionName)
 2256                 )
 2257             ParamValue = Value * openff.units.unit.nanometer
 2258         elif re.match("^SolvationSolventPadding$", ParamName, re.I):
 2259             #  float > 0 and units nanometer or none
 2260             if re.match("^None$", Value, re.I):
 2261                 ParamValue = None
 2262             else:
 2263                 if not MiscUtil.IsFloat(Value):
 2264                     MiscUtil.PrintError(
 2265                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2266                         % (Value, ParamName, ParamsOptionName)
 2267                     )
 2268                 Value = float(Value)
 2269                 if Value <= 0:
 2270                     MiscUtil.PrintError(
 2271                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 2272                         % (ParamValue, ParamName, ParamsOptionName)
 2273                     )
 2274                 ParamValue = Value * openff.units.unit.nanometer
 2275         elif re.match("^AlchemicalSoftcoreLJ$", ParamName, re.I):
 2276             if not re.match("^(Gapsys|Beutler)$", Value, re.I):
 2277                 MiscUtil.PrintError(
 2278                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is may be a valid OpenFE value. Supported values: Gapsys or Beutler'
 2279                     % (Value, Name, ParamsOptionName)
 2280                 )
 2281             ParamValue = Value.lower()
 2282         elif re.match("^EngineComputePlatform$", ParamName, re.I):
 2283             if not re.match("^(CPU|CUDA|OpenCL|Reference)$", Value, re.I):
 2284                 MiscUtil.PrintError(
 2285                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: CPU, CUDA, OpenCL, or Reference'
 2286                     % (Value, Name, ParamsOptionName)
 2287                 )
 2288             ParamValue = Value
 2289         elif re.match("^EngineGpuDeviceIndex$", ParamName, re.I):
 2290             #  Comma delimited string values...
 2291             DeviceIndices = Value.split()
 2292             if len(DeviceIndices) == 0:
 2293                 MiscUtil.PrintError(
 2294                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain space delimited list of device indices.\n'
 2295                     % (Value, ParamName, ParamsOptionName)
 2296                 )
 2297             for DeviceIndex in DeviceIndices:
 2298                 if not MiscUtil.IsInteger(DeviceIndex):
 2299                     MiscUtil.PrintError(
 2300                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 2301                         % (DeviceIndex, ParamName, ParamsOptionName)
 2302                     )
 2303                 DeviceIndices = [int(DeviceIndex) for DeviceIndex in DeviceIndices]
 2304             ParamValue = DeviceIndices
 2305         elif re.match("^Forcefields$", ParamName, re.I):
 2306             #  List of string values.....
 2307             Values = Value.split()
 2308             if len(Values) == 0:
 2309                 MiscUtil.PrintError(
 2310                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain a space delimited list of values..\n'
 2311                     % (Value, ParamName, ParamsOptionName)
 2312                 )
 2313             ParamValue = Values
 2314         elif re.match("^ForcefieldConstraints$", ParamName, re.I):
 2315             if not re.match("^(HBonds|AllBonds|HAngles|None)$", Value, re.I):
 2316                 MiscUtil.PrintError(
 2317                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: HBonds, AllBonds, HAngles, or None'
 2318                     % (Value, Name, ParamsOptionName)
 2319                 )
 2320             ParamValue = None if re.match("^None$", Value, re.I) else Value.lower()
 2321         elif re.match("^ForcefieldNonbondedMethod$", ParamName, re.I):
 2322             if not re.match("^(PME|NoCutoff)$", Value, re.I):
 2323                 MiscUtil.PrintError(
 2324                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is may be a valid OpenFE value. Supported values: PME or NoCutoff'
 2325                     % (Value, Name, ParamsOptionName)
 2326                 )
 2327             ParamValue = Value.lower()
 2328         elif re.match("^PartialChargeOffToolkitBackend$", ParamName, re.I):
 2329             if not re.match("^(AmberTools|OpenEye|RDKit)$", Value, re.I):
 2330                 MiscUtil.PrintError(
 2331                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: AmberTools, OpenEye, or RDKit'
 2332                     % (Value, Name, ParamsOptionName)
 2333                 )
 2334             ParamValue = Value.lower()
 2335         elif re.match("^PartialChargeMethod$", ParamName, re.I):
 2336             if not re.match("^(AM1BCC|AM1BCCELF10|Espaloma|NAGL)$", Value, re.I):
 2337                 MiscUtil.PrintError(
 2338                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: AM1BCC, AM1BCCELF10, Espaloma, or NAGL'
 2339                     % (Value, Name, ParamsOptionName)
 2340                 )
 2341             ParamValue = Value.lower()
 2342         elif re.match("^IntegratorBarostatFrequency$", ParamName, re.I):
 2343             if not MiscUtil.IsFloat(Value):
 2344                 MiscUtil.PrintError(
 2345                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2346                     % (Value, ParamName, ParamsOptionName)
 2347                 )
 2348             Value = float(Value)
 2349             if Value <= 0:
 2350                 MiscUtil.PrintError(
 2351                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 2352                     % (ParamValue, ParamName, ParamsOptionName)
 2353                 )
 2354             ParamValue = Value * openff.units.unit.timestep
 2355         elif re.match("^IntegratorLangevinCollisionRate$", ParamName, re.I):
 2356             if not MiscUtil.IsFloat(Value):
 2357                 MiscUtil.PrintError(
 2358                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2359                     % (Value, ParamName, ParamsOptionName)
 2360                 )
 2361             Value = float(Value)
 2362             if Value <= 0:
 2363                 MiscUtil.PrintError(
 2364                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 2365                     % (ParamValue, ParamName, ParamsOptionName)
 2366                 )
 2367             ParamValue = Value / openff.units.unit.picosecond
 2368         elif re.match("^IntegratorTimestep$", ParamName, re.I):
 2369             # float > 0 femtosecond
 2370             if not MiscUtil.IsFloat(Value):
 2371                 MiscUtil.PrintError(
 2372                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2373                     % (Value, ParamName, ParamsOptionName)
 2374                 )
 2375             Value = float(Value)
 2376             if Value <= 0:
 2377                 MiscUtil.PrintError(
 2378                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 2379                     % (ParamValue, ParamName, ParamsOptionName)
 2380                 )
 2381             ParamValue = Value * openff.units.unit.femtosecond
 2382         elif re.match(
 2383             "^(OutputPositionsWriteFrequency|SimulationRealTimeAnalysisMinimumTime|SimulationTimePerIteration)$",
 2384             ParamName,
 2385             re.I,
 2386         ):
 2387             #  float > 0 picosecond
 2388             if not MiscUtil.IsFloat(Value):
 2389                 MiscUtil.PrintError(
 2390                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2391                     % (Value, ParamName, ParamsOptionName)
 2392                 )
 2393             Value = float(Value)
 2394             if Value <= 0:
 2395                 MiscUtil.PrintError(
 2396                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 2397                     % (ParamValue, ParamName, ParamsOptionName)
 2398                 )
 2399             ParamValue = Value * openff.units.unit.picosecond
 2400         elif re.match("^(OutputCheckpointInterval|SimulationEquilibrationLength|SimulationProductionLength)$", ParamName, re.I):
 2401             #  float > 0 nanosecond
 2402             if not MiscUtil.IsFloat(Value):
 2403                 MiscUtil.PrintError(
 2404                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2405                     % (Value, ParamName, ParamsOptionName)
 2406                 )
 2407             Value = float(Value)
 2408             if Value <= 0:
 2409                 MiscUtil.PrintError(
 2410                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 2411                     % (ParamValue, ParamName, ParamsOptionName)
 2412                 )
 2413             ParamValue = Value * openff.units.unit.nanosecond
 2414         elif re.match("^(OutputVelocitiesWriteFrequency|SimulationRealTimeAnalysisInterval)$", ParamName, re.I):
 2415             #  float > 0 picosecond or none
 2416             if re.match("^None$", Value, re.I):
 2417                 ParamValue = None
 2418             else:
 2419                 if not MiscUtil.IsFloat(Value):
 2420                     MiscUtil.PrintError(
 2421                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2422                         % (Value, ParamName, ParamsOptionName)
 2423                     )
 2424                 Value = float(Value)
 2425                 if Value <= 0:
 2426                     MiscUtil.PrintError(
 2427                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 2428                         % (ParamValue, ParamName, ParamsOptionName)
 2429                     )
 2430                 ParamValue = Value * openff.units.unit.picosecond
 2431         elif re.match("^LambdaFunctions$", ParamName, re.I):
 2432             if not re.match("^(default|namd|quarters)$", Value, re.I):
 2433                 MiscUtil.PrintError(
 2434                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: default, namd, or quarters'
 2435                     % (Value, Name, ParamsOptionName)
 2436                 )
 2437             ParamValue = Value.lower()
 2438         elif re.match("^SimulationSamplerMethod$", ParamName, re.I):
 2439             if not re.match("^(repex|sams|independent)$", Value, re.I):
 2440                 MiscUtil.PrintError(
 2441                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: repex, sams, or independent'
 2442                     % (Value, Name, ParamsOptionName)
 2443                 )
 2444             ParamValue = Value.lower()
 2445         elif re.match("^SimulationSamsFlatnessCriteria$", ParamName, re.I):
 2446             if not re.match("^(logz-flatness|minimum-visits|histogram-flatness)$", Value, re.I):
 2447                 MiscUtil.PrintError(
 2448                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: logz-flatness, minimum-visits, or histogram-flatness'
 2449                     % (Value, Name, ParamsOptionName)
 2450                 )
 2451             ParamValue = Value.lower()
 2452         elif re.match("^SolvationBoxShape$", ParamName, re.I):
 2453             if not re.match("^(cube|dodecahedron|octahedron)$", Value, re.I):
 2454                 MiscUtil.PrintError(
 2455                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: cube, dodecahedron, or octahedron'
 2456                     % (Value, Name, ParamsOptionName)
 2457                 )
 2458             ParamValue = Value.lower()
 2459         elif re.match("^SolvationSolventModel$", ParamName, re.I):
 2460             if not re.match("^(tip3p|spce|tip4pew|tip5p)$", Value, re.I):
 2461                 MiscUtil.PrintError(
 2462                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: tip3p, spce, tip4pew, or tip5p'
 2463                     % (Value, Name, ParamsOptionName)
 2464                 )
 2465             ParamValue = Value.lower()
 2466         elif re.match("^SimulationEarlyTerminationTargetError$", ParamName, re.I):
 2467             # float >= 0 units kilocalorie_per_mole
 2468             if not MiscUtil.IsFloat(Value):
 2469                 MiscUtil.PrintError(
 2470                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2471                     % (Value, ParamName, ParamsOptionName)
 2472                 )
 2473             Value = float(Value)
 2474             if Value < 0:
 2475                 MiscUtil.PrintError(
 2476                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 2477                     % (ParamValue, ParamName, ParamsOptionName)
 2478                 )
 2479             ParamValue = Value * openff.units.unit.kilocalorie_per_mole
 2480         elif re.match("^SolvationBoxSize$", ParamName, re.I):
 2481             # List of X, Y, Z values...
 2482             if re.match("^None$", Value, re.I):
 2483                 ParamValue = None
 2484             else:
 2485                 Values = Value.split()
 2486                 if len(Values) != 3:
 2487                     MiscUtil.PrintError(
 2488                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain a set of three space delimited values.\n'
 2489                         % (Value, ParamName, ParamsOptionName)
 2490                     )
 2491                 for Value in Values:
 2492                     if not MiscUtil.IsFloat(Value):
 2493                         MiscUtil.PrintError(
 2494                             'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2495                             % (Value, ParamName, ParamsOptionName)
 2496                         )
 2497                 Values = [float(Value) for Value in Values]
 2498                 ParamValue = Values * openff.units.unit.nanometer
 2499         elif re.match("^ThermoPressure$", ParamName, re.I):
 2500             #  float > 0 and units bar
 2501             if not MiscUtil.IsFloat(Value):
 2502                 MiscUtil.PrintError(
 2503                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2504                     % (Value, ParamName, ParamsOptionName)
 2505                 )
 2506             Value = float(Value)
 2507             if Value <= 0:
 2508                 MiscUtil.PrintError(
 2509                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 2510                     % (ParamValue, ParamName, ParamsOptionName)
 2511                 )
 2512             ParamValue = Value * openff.units.unit.bar
 2513         elif re.match("^ThermoTemperature$", ParamName, re.I):
 2514             # float >= 0 and units kelvin
 2515             if not MiscUtil.IsFloat(Value):
 2516                 MiscUtil.PrintError(
 2517                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 2518                     % (Value, ParamName, ParamsOptionName)
 2519                 )
 2520             Value = float(Value)
 2521             if Value < 0:
 2522                 MiscUtil.PrintError(
 2523                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: >= 0\n'
 2524                     % (ParamValue, ParamName, ParamsOptionName)
 2525                 )
 2526             ParamValue = Value * openff.units.unit.kelvin
 2527         else:
 2528             # Str or None...
 2529             ParamValue = None if re.match("^None$", Value, re.I) else Value
 2530 
 2531         # Set value...
 2532         ParamsInfo[ParamName] = ParamValue
 2533 
 2534     # Handle parameters with possible auto values...
 2535     _ProcessOptionOpenFERelativeFreeEnergyParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 2536 
 2537     return ParamsInfo
 2538 
 2539 
 2540 def _ProcessOptionOpenFERelativeFreeEnergyParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 2541     """Process parameters with possible auto values and perform validation."""
 2542 
 2543     # Validate solvation parameter values...
 2544     ParamName1 = "SolvationBoxSize"
 2545     ParamValue1 = ParamsInfo[ParamName1]
 2546     ParamName2 = "SolvationSolventPadding"
 2547     ParamValue2 = ParamsInfo[ParamName2]
 2548     if ParamsInfo[ParamName1] is not None and ParamsInfo[ParamName2] is not None:
 2549         MiscUtil.PrintError(
 2550             'The parameter values, %s and %s, specified for parameter names, %s and %s, using "%s" option is not a valid value. You must specify only one of these values.\n'
 2551             % (ParamValue1, ParamValue2, ParamName1, ParamName2, ParamsOptionName)
 2552         )
 2553 
 2554     _ProcessPartialChargeMethodRelativeFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 2555     _ProcessPartialChargeNaglRelativeFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 2556 
 2557 
 2558 def _ProcessPartialChargeMethodRelativeFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 2559     """Process  PartialChargeMethod RFE paramater."""
 2560 
 2561     _ProcessPartialChargeMethodFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 2562 
 2563 
 2564 def _ProcessPartialChargeNaglRelativeFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 2565     """Process  PartialChargeNaglModel RFE paramater."""
 2566 
 2567     _ProcessPartialChargeNaglFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 2568 
 2569 
 2570 def _SetupMapForRelativeFreeEnergyParameters():
 2571     """Map relative free energy option paramater names to OpenFE relative
 2572     free energy settings.
 2573     """
 2574 
 2575     RFEParametersMap = {
 2576         "ProtocolRepeats": [None, "protocol_repeats"],
 2577         "AlchemicalEndstateDispersionCorrection": ["alchemical_settings", "endstate_dispersion_correction"],
 2578         "AlchemicalExplicitChargeCorrection": ["alchemical_settings", "explicit_charge_correction"],
 2579         "AlchemicalExplicitChargeCorrectionCutoff": ["alchemical_settings", "explicit_charge_correction_cutoff"],
 2580         "AlchemicalSoftcoreLJ": ["alchemical_settings", "softcore_LJ"],
 2581         "AlchemicalSoftcoreAlpha": ["alchemical_settings", "softcore_alpha"],
 2582         "AlchemicalTurnOffCoreUniqueExceptions": ["alchemical_settings", "turn_off_core_unique_exceptions"],
 2583         "AlchemicalUseDispersionCorrection": ["alchemical_settings", "use_dispersion_correction"],
 2584         "EngineComputePlatform": ["engine_settings", "compute_platform"],
 2585         "EngineGpuDeviceIndex": ["engine_settings", "gpu_device_index"],
 2586         "ForcefieldConstraints": ["forcefield_settings", "constraints"],
 2587         "Forcefields": ["forcefield_settings", "forcefields"],
 2588         "ForcefieldHydrogenMass": ["forcefield_settings", "hydrogen_mass"],
 2589         "ForcefieldNonbondedCutoff": ["forcefield_settings", "nonbonded_cutoff"],
 2590         "ForcefieldNonbondedMethod": ["forcefield_settings", "nonbonded_method"],
 2591         "ForcefieldRigidWater": ["forcefield_settings", "rigid_water"],
 2592         "ForcefieldSmallMoleculeForcefield": ["forcefield_settings", "small_molecule_forcefield"],
 2593         "IntegratorBarostatFrequency": ["integrator_settings", "barostat_frequency"],
 2594         "IntegratorConstraintTolerance": ["integrator_settings", "constraint_tolerance"],
 2595         "IntegratorLangevinCollisionRate": ["integrator_settings", "langevin_collision_rate"],
 2596         "IntegratorNRestartAttempts": ["integrator_settings", "n_restart_attempts"],
 2597         "IntegratorReassignVelocities": ["integrator_settings", "reassign_velocities"],
 2598         "IntegratorRemoveCom": ["integrator_settings", "remove_com"],
 2599         "IntegratorTimestep": ["integrator_settings", "timestep"],
 2600         "LambdaFunctions": ["lambda_settings", "lambda_functions"],
 2601         "LambdaWindows": ["lambda_settings", "lambda_windows"],
 2602         "OutputCheckpointInterval": ["output_settings", "checkpoint_interval"],
 2603         "OutputCheckpointStorageFilename": ["output_settings", "checkpoint_storage_filename"],
 2604         "OutputForcefieldCache": ["output_settings", "forcefield_cache"],
 2605         "OutputFilename": ["output_settings", "output_filename"],
 2606         "OutputIndices": ["output_settings", "output_indices"],
 2607         "OutputStructure": ["output_settings", "output_structure"],
 2608         "OutputPositionsWriteFrequency": ["output_settings", "positions_write_frequency"],
 2609         "OutputVelocitiesWriteFrequency": ["output_settings", "velocities_write_frequency"],
 2610         "PartialChargeNaglModel": ["partial_charge_settings", "nagl_model"],
 2611         "PartialChargeNumberOfConformers": ["partial_charge_settings", "number_of_conformers"],
 2612         "PartialChargeOffToolkitBackend": ["partial_charge_settings", "off_toolkit_backend"],
 2613         "PartialChargeMethod": ["partial_charge_settings", "partial_charge_method"],
 2614         "SimulationEarlyTerminationTargetError": ["simulation_settings", "early_termination_target_error"],
 2615         "SimulationEquilibrationLength": ["simulation_settings", "equilibration_length"],
 2616         "SimulationMinimizationSteps": ["simulation_settings", "minimization_steps"],
 2617         "SimulationNReplicas": ["simulation_settings", "n_replicas"],
 2618         "SimulationProductionLength": ["simulation_settings", "production_length"],
 2619         "SimulationRealTimeAnalysisInterval": ["simulation_settings", "real_time_analysis_interval"],
 2620         "SimulationRealTimeAnalysisMinimumTime": ["simulation_settings", "real_time_analysis_minimum_time"],
 2621         "SimulationSamplerMethod": ["simulation_settings", "sampler_method"],
 2622         "SimulationSamsFlatnessCriteria": ["simulation_settings", "sams_flatness_criteria"],
 2623         "SimulationSamsGamma0": ["simulation_settings", "sams_gamma0"],
 2624         "SimulationTimePerIteration": ["simulation_settings", "time_per_iteration"],
 2625         "SolvationBoxShape": ["solvation_settings", "box_shape"],
 2626         "SolvationBoxSize": ["solvation_settings", "box_size"],
 2627         "SolvationSolventModel": ["solvation_settings", "solvent_model"],
 2628         "SolvationSolventPadding": ["solvation_settings", "solvent_padding"],
 2629         "ThermoPh": ["thermo_settings", "ph"],
 2630         "ThermoPressure": ["thermo_settings", "pressure"],
 2631         "ThermoRedoxPotential": ["thermo_settings", "redox_potential"],
 2632         "ThermoTemperature": ["thermo_settings", "temperature"],
 2633     }
 2634 
 2635     return RFEParametersMap
 2636 
 2637 
 2638 def _SetupRelativeFreeEnergyDefaultParametersInfo(ParamsOptionName, ParamsOptionValue):
 2639     """Setup RFE default parameters information using the current RFE settings."""
 2640 
 2641     ParamsInfo = {}
 2642 
 2643     RBFESettings = openfe.protocols.openmm_rfe.RelativeHybridTopologyProtocol.default_settings()
 2644     RFEParametersMap = _SetupMapForRelativeFreeEnergyParameters()
 2645 
 2646     for ParamName in RFEParametersMap.keys():
 2647         RFEParamGroupName, RFEParamName = RFEParametersMap[ParamName]
 2648         if RFEParamGroupName is None:
 2649             if hasattr(RBFESettings, RFEParamName):
 2650                 ParamsInfo[ParamName] = getattr(RBFESettings, RFEParamName)
 2651             else:
 2652                 MiscUtil.PrintInfo(
 2653                     'The OpenFE RFE settings name, %s, corresponding to RFE parameter name, %s, specified using option "%s" is not available. Ignoring parameter...'
 2654                     % (RFEParamName, ParamName, ParamsOptionName)
 2655                 )
 2656         else:
 2657             RFEParamGroupSettings = (
 2658                 getattr(RBFESettings, RFEParamGroupName) if hasattr(RBFESettings, RFEParamGroupName) else None
 2659             )
 2660             if RFEParamGroupSettings is not None and hasattr(RFEParamGroupSettings, RFEParamName):
 2661                 ParamsInfo[ParamName] = getattr(RFEParamGroupSettings, RFEParamName)
 2662             else:
 2663                 MiscUtil.PrintInfo(
 2664                     'The OpenFE RFE parameter name, %s, for settings, %s, corresponding to RFE parameter name, %s, specified using option "%s" is not available. Ignoring parameter...'
 2665                     % (RFEParamName, RFEParamGroupName, ParamName, ParamsOptionName)
 2666                 )
 2667 
 2668     return ParamsInfo
 2669 
 2670 
 2671 def SetupRelativeFreeEnergySettings(ParamsOptionName, ParamsInfo):
 2672     """Setup relative free energy protocol settings to calculate RBFE.
 2673 
 2674     The ParamsInfo is a comma delimited list of parameter name and value pairs
 2675     returned by ProcessOptionOpenFERelativeFreeEnergyParameters().
 2676 
 2677     Arguments:
 2678         ParamsOptionName (str): Command line OpenFE RFE parameters option name.
 2679         ParamsInfo (dict): Parameter name and value pairs.
 2680 
 2681     Returns:
 2682         object: OpenFE RelativeHybridTopologyProtocol settings object.
 2683 
 2684     """
 2685 
 2686     RBFESettings = openfe.protocols.openmm_rfe.RelativeHybridTopologyProtocol.default_settings()
 2687     RFEParametersMap = _SetupMapForRelativeFreeEnergyParameters()
 2688 
 2689     _UpdateOpenFESettings("RBFE", ParamsOptionName, ParamsInfo, RBFESettings, RFEParametersMap)
 2690 
 2691     return RBFESettings
 2692 
 2693 
 2694 def UpdateRelativeFreeEnergySettingsForChargeCorrection(ParamsOptionName, ParamsInfo, RBFESettings):
 2695     """Update relative free energy protocol settings for charge correction.
 2696 
 2697     The ParamsInfo is a comma delimited list of parameter name and value pairs
 2698     returned by ProcessOptionOpenFERelativeFreeEnergyChargeCorrectionParameters().
 2699 
 2700     Arguments:
 2701         ParamsOptionName (str): Command line OpenFE RBFE charge correction
 2702             parameters option name.
 2703         ParamsInfo (dict): Parameter name and value pairs.
 2704         RBFESettings (dict): OpenFE RelativeHybridTopologyProtocol settings object.
 2705 
 2706     Returns:
 2707         None
 2708 
 2709     """
 2710 
 2711     RFEParametersMap = _SetupMapForRelativeFreeEnergyParameters()
 2712 
 2713     for ParamName in ParamsInfo.keys():
 2714         if ParamName in RFEParametersMap:
 2715             RFEParamGroupName, RFEParamName = RFEParametersMap[ParamName]
 2716             if RFEParamGroupName is None:
 2717                 if hasattr(RBFESettings, RFEParamName):
 2718                     setattr(RBFESettings, RFEParamName, ParamsInfo[ParamName])
 2719                 else:
 2720                     MiscUtil.PrintInfo(
 2721                         'The charge correction RFE parameter name, %s, specified using option "%s" is not valud. Ignoring parameter...'
 2722                         % (ParamName, ParamsOptionName)
 2723                     )
 2724             else:
 2725                 RFEParamGroupSettings = (
 2726                     getattr(RBFESettings, RFEParamGroupName) if hasattr(RBFESettings, RFEParamGroupName) else None
 2727                 )
 2728                 if RFEParamGroupSettings is not None and hasattr(RFEParamGroupSettings, RFEParamName):
 2729                     setattr(RFEParamGroupSettings, RFEParamName, ParamsInfo[ParamName])
 2730                 else:
 2731                     MiscUtil.PrintInfo(
 2732                         'The charge correction RFE parameter name, %s, specified using option "%s" is not valud. Ignoring parameter...'
 2733                         % (ParamName, ParamsOptionName)
 2734                     )
 2735         else:
 2736             MiscUtil.PrintInfo(
 2737                 'The charge correction RFE parameter name, %s, specified using option "%s" is not valud. Ignoring parameter...'
 2738                 % (ParamName, ParamsOptionName)
 2739             )
 2740 
 2741 
 2742 def UpdateRelativeFreeEnergySettingsForVacuum(ParamsOptionName, ParamsInfo, RBFESettings):
 2743     """Update relative free energy protocol settings for vacuum.
 2744 
 2745     The ParamsInfo is a comma delimited list of parameter name and value pairs
 2746     returned by ProcessOptionOpenFERelativeFreeEnergyVacuumParameters().
 2747 
 2748     Arguments:
 2749         ParamsOptionName (str): Command line OpenFE RBFE vacuum parameters
 2750             option name.
 2751         ParamsInfo (dict): Parameter name and value pairs.
 2752         RBFESettings (dict): OpenFE RelativeHybridTopologyProtocol settings object.
 2753 
 2754     Returns:
 2755         None
 2756 
 2757     """
 2758 
 2759     RFEParametersMap = _SetupMapForRelativeFreeEnergyParameters()
 2760 
 2761     for ParamName in ParamsInfo.keys():
 2762         if ParamName in RFEParametersMap:
 2763             RFEParamGroupName, RFEParamName = RFEParametersMap[ParamName]
 2764             if RFEParamGroupName is None:
 2765                 if hasattr(RBFESettings, RFEParamName):
 2766                     setattr(RBFESettings, RFEParamName, ParamsInfo[ParamName])
 2767                 else:
 2768                     MiscUtil.PrintInfo(
 2769                         'The vacuum RFE parameter name, %s, specified using option "%s" is not valud. Ignoring parameter...'
 2770                         % (ParamName, ParamsOptionName)
 2771                     )
 2772             else:
 2773                 RFEParamGroupSettings = (
 2774                     getattr(RBFESettings, RFEParamGroupName) if hasattr(RBFESettings, RFEParamGroupName) else None
 2775                 )
 2776                 if RFEParamGroupSettings is not None and hasattr(RFEParamGroupSettings, RFEParamName):
 2777                     setattr(RFEParamGroupSettings, RFEParamName, ParamsInfo[ParamName])
 2778                 else:
 2779                     MiscUtil.PrintInfo(
 2780                         'The vacuum RFE parameter name, %s, specified using option "%s" is not valud. Ignoring parameter...'
 2781                         % (ParamName, ParamsOptionName)
 2782                     )
 2783         else:
 2784             MiscUtil.PrintInfo(
 2785                 'The vacuum RFE parameter name, %s, specified using option "%s" is not valud. Ignoring parameter...'
 2786                 % (ParamName, ParamsOptionName)
 2787             )
 2788 
 2789 
 2790 def ProcessOptionOpenFERelativeFreeEnergySeparatedTopologyParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo=None):
 2791     """Process parameters for RBFE parameters option and return a map
 2792     containing processed parameter names and values.
 2793 
 2794     The ParamsOptionValue is a comma delimited list of parameter name and value pairs
 2795     to setup RBFE calculations using a separated topologies approach.
 2796 
 2797     The default values are automatically updated to match settings provided by
 2798     OpenFE module SepTopProtocol.
 2799 
 2800     You must specify valid OpenFE values for these parameters. An extensive
 2801     validation is not performed.
 2802 
 2803     The supported parameter names along with their default and possible
 2804     values are shown below:
 2805 
 2806         protocolRepeats, 3
 2807 
 2808         Complex equil output settings:
 2809         
 2810         complexEquilOutputCheckpointInterval, 1  [ Units: nanosecond ]
 2811         complexEquilOutputCheckpointStorageFilename, checkpoint.chk
 2812         complexEquilOutputEquilNPTStructure, equil_npt.pdb
 2813         complexEquilOutputEquilNVTstructure, None
 2814         complexEquilOutputForcefieldCache, db.json
 2815         complexEquilOutputLogOutput, equil_simulation.log
 2816         complexEquilOutputMinimizedStructure, minimized.pdb
 2817         complexEquilOutputIndices, all  [ Possible value: Any valid
 2818             selection. ]
 2819         complexEquilOutputPreminimizedStructure, system.pdb
 2820         complexEquilOutputProductionTrajectoryFilename, production_equil.xtc
 2821         complexEquilOutputTrajectoryWriteInterval,  20.0  [ Units:
 2822             picosecond ]
 2823 
 2824         Complex equil simulation settings:
 2825 
 2826         complexEquilSimulationEquilibrationLength, 0.1 [ Units: nanosecond ]
 2827         complexEquilSimulationEquilibrationLengthNVT, 0.1   [ Units:
 2828             nanosecond ]
 2829         complexEquilSimulationMinimizationSteps, 5000
 2830         complexEquilSimulationProductionLength, 2.0  [ Units: nanosecond ]
 2831 
 2832         Complex lambda settings:
 2833 
 2834         complexLambdaElecA, 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.25 0.5 0.75
 2835             1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0  [ Possible values: A space
 2836             delimited list of values between 0.0 and 1.0 ]
 2837         complexLambdaElecB, 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0.75 0.5 0.25
 2838             0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 [ Possible values: A space
 2839             delimited list of values between 0.0 and 1.0 ]
 2840         complexLambdaRestraintsA, 0.0 0.05 0.1 0.3 0.5 0.75 1.0 1.0 1.0
 2841             1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 [ Possible values: A
 2842             space delimited list of values between 0.0 and 1.0 ]
 2843         complexLambdaRestraintsB, 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
 2844             1.0 1.0 1.0 0.75 0.5 0.3 0.1 0.05 0.0 [ Possible values: A space
 2845             delimited list of values between 0.0 and 1.0 ]
 2846         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
 2847             0.143 0.286 0.429 0.572 0.715 0.857 1.0 [ Possible values: A
 2848             delimited list of values between 0.0 and 1.0 ]
 2849         complexLambdaVdwB, 1.0 0.857 0.715 0.572 0.429 0.286 0.143 0.0
 2850             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
 2851             delimited list of values between 0.0 and 1.0 ]
 2852 
 2853         Complex output settings:
 2854 
 2855         complexOutputCheckpointInterval, 1.0  [ Units: nanosecond ]
 2856         complexOutputCheckpointStorageFilename, complex_checkpoint.nc
 2857         complexOutputForcefieldCache, db.json
 2858         complexOutputFilename, complex.nc
 2859         complexOutputIndices, not water  [ Possible value: Any valid
 2860             selection. ]
 2861         complexOutputStructure, alchemical_system.pdb
 2862         complexOutputPositionsWriteFrequency, 100.0  [ Units: picosecond ]
 2863         complexOutputVelocitiesWriteFrequency, None  [ Possible
 2864             values: > 0; Units: picosecond ]
 2865 
 2866         Complex restraint settings:
 2867 
 2868         complexRestraintKPhiA, 334.72  [ Units: kilojoule_per_mole/radian**2
 2869             The default value is equivalent to 80 kcal/mol/radian**2 ]
 2870         complexRestraintKPhiB, 334.72  [ Units: kilojoule_per_mole/radian**2
 2871             The default value is equivalent to 80 kcal/mol/radian**2 ]
 2872         complexRestraintKPhiC, 334.72  [ Units: kilojoule_per_mole/radian**2
 2873             The default value is equivalent to 80 kcal/mol/radian**2 ]
 2874         complexRestraintKR, 4184.0  [ Units: kilojoule_per_mole/nanometer**2
 2875                 The default value is equivalent to 10 kcal/mol/angstrom**2
 2876         complexRestraintKThetaA, 334.72  [ Units:kilojoule_per_mole/radian**2
 2877             The default value is equivalent to 80 kcal/mol/radian**2 ]
 2878         complexRestraintKThetaB, 334.72  [ Units:kilojoule_per_mole/radian**2
 2879             The default value is equivalent to 80 kcal/mol/radian**2 ]
 2880         complexRestraintAnchorFindingStrategy, bonded  [ Possible values:
 2881             multi-residue or bonded ] 
 2882         complexRestraintDsspFilter, yes   [ Possible values: yes or no ]
 2883         complexRestraintHostMaxDistance, 1.5  [ Units: nanometer ]
 2884         complexRestraintHostMinDistance, 0.5  [ Units: nanometer ]
 2885         complexRestraintHostSelection, backbone   [ Possible value: Any valid
 2886             selection. ]
 2887         complexRestraintRmsfCutoff, 0.1  [ Units: nanometer ]
 2888 
 2889         Complex simulation settings:
 2890 
 2891         complexSimulationEarlyTerminationTargetError, 0.0  [ Units:
 2892             kilocalorie_per_mole ]
 2893         complexSimulationEquilibrationLength,  1.0  [ Units: nanosecond ]
 2894         complexSimulationMinimizationSteps, 5000
 2895         complexSimulationNReplicas, 19
 2896         complexSimulationProductionLength, 10.0  [ Units: nanosecond ]
 2897         complexSimulationRealTimeAnalysisInterval, 250.0  [ Units:
 2898             picosecond ]
 2899         complexSimulationRealTimeAnalysisMinimumTime, 500.0  [ Units:
 2900             picosecond ]
 2901         complexSimulationSamplerMethod, repex  [ Possible values: repex,
 2902             sams, or independent ]
 2903         complexSimulationSamsFlatnessCriteria, logZ-flatness  [ Possible
 2904             values: logZ-flatness, minimum-visits or histogram-flatness ]
 2905         complexSimulationSamsGamma0, 1.0
 2906         complexSimulationTimePerIteration, 2.5   [ Units: picosecond ]
 2907 
 2908         Complex solvation settings:
 2909 
 2910         complexSolvationBoxShape, dodecahedron  [  Possible values: cube,
 2911             dodecahedron, or octahedron ]
 2912         complexSolvationBoxSize, None  [ Possible value: A triplet of space
 2913             X Y Z values; Units: nanometer ]
 2914         complexSolvationSolventModel, tip3p  [ Possible values: tip3p, spce,
 2915             tip4pew, or tip5p ]
 2916         complexSolvationSolventPadding, 1.0  [ Units: nanometer ]
 2917 
 2918         Engine settings:
 2919 
 2920         engineComputePlatform, CPU  [ Possible values: CPU, CUDA,
 2921             OpenCL, or Reference ]
 2922         engineGpuDeviceIndex, None [ Possible values: 0, 0 1, etc. ]
 2923 
 2924         Forcefield settings:
 2925 
 2926         forcefieldConstraints, HBonds  [ Possible values: HBonds,
 2927             AllBonds, or HAngles ]
 2928         forcefields, amber/ff14SB.xml amber/tip3p_standard.xml
 2929             amber/tip3p_HFE_multivalent.xml amber/phosaa10.xml
 2930             [ Possible values: A space delimited list of valid names. ]
 2931         forcefieldHydrogenMass, 3.0  [ Units: amu ]
 2932         forcefieldNonbondedCutoff, 0.9   [ Units: nanometer ]
 2933         forcefieldNonbondedMethod, PME  [ Possible values: PME or
 2934             NoCutoff ]
 2935         forcefieldRigidWater, yes  [ Possible values: yes or no ]
 2936         forcefieldSmallMoleculeForcefield, openff-2.1.1  [ Possible
 2937             value: A valid forcefield name. ]
 2938 
 2939         Integrator settings:
 2940         
 2941         integratorBarostatFrequency, 25.0 * timestep  [ The specified value
 2942             is a multiple of integratorTimestep. ]
 2943         integratorConstraintTolerance, 1e-06
 2944         integratorLangevinCollisionRate, 1.0  [ Units: 1 / picosecond ]
 2945         integratorNRestartAttempts, 20
 2946         integratorReassignVelocities, no  [ Possible values: yes or no ]
 2947         integratorRemoveCom, no  [ Possible values: yes or no ]
 2948         integratorTimestep, 4.0 [ Units: femtosecond ] 
 2949 
 2950         Partial charge settings:
 2951 
 2952         partialChargeNaglModel, None  [ Default: Production AM1BCC model for
 2953             NAGL; Possible value: Any valid name. ]
 2954         partialChargeNumberOfConformers, None  [ Possible value: > 0 ]
 2955         partialChargeOffToolkitBackend, AmberTools  [ Possible values:
 2956             AmberTools or RDKit ]
 2957         partialChargeMethod, AM1BCC  [ Possble values: AM1BCC, Espaloma,
 2958             or NAGL ]
 2959 
 2960         Solvent equil output settings:
 2961 
 2962         solventEquilOutputCheckpointInterval, 1.0  [ Units: nanosecond ]
 2963         solventEquilOutputCheckpointStorageFilename, checkpoint.chk
 2964         solventEquilOutputEquilNPTStructure, equil_npt.pdb
 2965         solventEquilOutputEquilNVTstructure, None
 2966         solventEquilOutputForcefieldCache, db.json
 2967         solventEquilOutputLogOutput, equil_simulation.log
 2968         solventEquilOutputMinimizedStructure, minimized.pdb
 2969         solventEquilOutputIndices, all  [  Possible value: Any valid
 2970             selection. ]
 2971         solventEquilOutputPreminimizedStructure, system.pdb
 2972         solventEquilOutputProductionTrajectoryFilename, equil_npt.xtc
 2973         solventEquilOutputTrajectoryWriteInterval, 20.0  [ Units:
 2974             picosecond ]
 2975 
 2976         Solvent_equil_simulation_settings:
 2977 
 2978         solventEquilSimulationEquilibrationLength, 0.1 [ Units: nanosecond ]
 2979         solventEquilSimulationEquilibrationLengthNVT, 0.1  [ Units:
 2980             nanosecond ]
 2981         solventEquilSimulationMinimizationSteps, 5000
 2982         solventEquilSimulationProductionLength, 2.0  [ Units: nanosecond ]
 2983 
 2984         Solvent lambda settings:
 2985 
 2986         solventLambdaElecA, 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.125
 2987             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
 2988             1.0 1.0 1.0 [ Possible values: A space delimited list of values
 2989             between 0.0 and 1.0 ]
 2990         solventLambdaElecB, 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0.875
 2991             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
 2992             0.0 0.0 0.0 [ Possible values: A space delimited list of values
 2993                 between 0.0 and 1.0 ]
 2994         solventLambdaRestraintsA, 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
 2995             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
 2996             0.0 [ Possible values: A space delimited list of values between
 2997             0.0 and 1.0 ]
 2998         solventLambdaRestraintsB, 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
 2999             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
 3000             0.0 [ Possible values: A space delimited list of values between
 3001             0.0 and 1.0 ]
 3002         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
 3003             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
 3004             1.0 [ Possible values: A space delimited list of values between
 3005                 0.0 and 1.0 ]
 3006         solventLambdaVdwB, 1.0 0.85 0.77 0.7 0.6 0.48 0.36 0.24 0.12 0.0
 3007             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
 3008             0.0 [ Possible values: A space delimited list of values between
 3009             0.0 and 1.0 ]
 3010 
 3011         Solvent output settings:
 3012 
 3013         solventOutputCheckpointInterval, 1.0  [ Units: nanosecond ]
 3014         solventOutputCheckpointStorageFilename, solvent_checkpoint.nc
 3015         solventOutputForcefieldCache, db.json
 3016         solventOutputFilename, solvent.nc
 3017         solventOutputIndices, not water  [ Possible value: Any valid
 3018             selection. ]
 3019         solventOutputStructure, alchemical_system.pdb
 3020         solventOutputPositionsWriteFrequency, 100.0  [ Units: picosecond ]
 3021         solventOutputVelocitiesWriteFrequency, None  [ Possible
 3022             values: > 0; Units: picosecond ]
 3023 
 3024         Solvent restraint settings:
 3025         
 3026         solventRestraintCentralAtomsOnly, No  [ Possible values: yes or no ]
 3027         solventRestraintSpringConstant, 1000.0 [ Units: kilojoule_per_mole /
 3028             nanometer ** 2. The default value is equivalent to 2.40
 3029             kilocalorie_per_mole / angstromg ** 2 ]
 3030 
 3031         Solvent simulation settings:
 3032 
 3033         solventSimulationEarlyTerminationTargetError, 0.0  [ Units:
 3034             kilocalorie_per_mole ]
 3035         solventSimulationEquilibrationLength, 1.0  [ Units: nanosecond ]
 3036         solventSimulationMinimizationSteps, 5000
 3037         solventSimulationNReplicas, 27
 3038         solventSimulationProductionLength, 10.0  [ Units: nanosecond ]
 3039         solventSimulationRealTimeAnalysisInterval, 250.0  [ Unit: picosecond ]
 3040         solventSimulationRealTimeAnalysisMinimumTime, 500.0  [ Units:
 3041             picosecond ]
 3042         solventSimulationSamplerMethod, repex  [ Possible values: repex,
 3043             sams, or independent ]
 3044         solventSimulationSamsFlatnessCriteria, logZ-flatness  [ Possible
 3045             values: logZ-flatness, minimum-visits or histogram-flatness ]
 3046         solventSimulationSamsGamma0, 1.0
 3047         solventSimulationTimePerIteration, 2.5  [ Units: picosecond ]
 3048 
 3049         Solvent solvation settings:
 3050 
 3051         solventSolvationBoxShape, dodecahedron  [  Possible values: cube,
 3052             dodecahedron, or octahedron ]
 3053         solventSolvationBoxSize, None  [ Possible value: A triplet of space
 3054             X Y Z values; Units: nanometer ]
 3055         solventSolvationSolventModel, tip3p  [ Possible values: tip3p, spce,
 3056             tip4pew, or tip5p ]
 3057         solventSolvationSolventPadding, 1.5  [ Units: nanometer ]
 3058 
 3059         Thermo settings:
 3060         
 3061         thermoPh, None  [ Possible values: > 0 ]
 3062         thermoPressure, 1.0  [ Units: bar ]
 3063         thermoRedoxPotential, None  [ Possible values: A valid float.
 3064             Units: millivolts (mV) ]
 3065         thermoTemperature, 298.15  [ Units: kelvin ]
 3066 
 3067     A brief description of parameters, taken from OpenFE documentation, is
 3068     provided below:
 3069 
 3070         protocolRepeats: Number of completely independent repeats of the
 3071             entire sampling process.
 3072 
 3073         Complex settings:
 3074 
 3075         Complex parameters for the system, including the solvent model and
 3076         the solvent padding.
 3077 
 3078         Complex equil output settings:
 3079 
 3080         Parameters controlling simulation output during equilibration
 3081         phase of complex transformation.
 3082 
 3083         complexEquilOutputCheckpointInterval: Frequency to write the
 3084             checkpoint file.
 3085         complexEquilOutputCheckpointStorageFilename: Checkpoint filename.
 3086         complexEquilOutputEquilNPTStructure: NPT structure filename.
 3087         complexEquilOutputEquilNVTstructure: NVT strucure filename.
 3088         complexEquilOutputForcefieldCache:  Filename for caching small
 3089             molecule residue templates.
 3090         complexEquilOutputLogOutput: Simulation log filename.
 3091         complexEquilOutputMinimizedStructure: Minimized structure filename.
 3092         complexEquilOutputIndices: Selection string for selecting
 3093             coordinates to write.
 3094         complexEquilOutputPremnimizedStructure: Initial structure filename.
 3095         complexEquilOutputProductionTrajectoryFilename: Trajectory filename.
 3096         complexEquilOutputTrajectoryWriteInterval: Frequency for writing
 3097             velocities to trajectory file.
 3098 
 3099         Complex equil simulation settings:
 3100 
 3101         Parameters controlling simulation during equilibration phase of
 3102         complex transformation.
 3103 
 3104         complexEquilSimulationEquilibrationLength:  Length of the NPT
 3105             equilibration phase.
 3106         complexEquilSimulationEquilibrationLengthNVT: Length of the NVT
 3107             equilibration phase.
 3108         complexEquilSimulationMinimizationSteps: Maximum number of
 3109             minimization steps to perform.
 3110         complexEquilSimulationProductionLength:  Length of the NPT
 3111             production phase.
 3112 
 3113         Complex lambda settings:
 3114         
 3115         Lambda protocol parameters for complex transformation.
 3116 
 3117         complexLambdaElecA: List of lambda values for electrostatics. The
 3118             values of 0 and 1 imply state A and state B respectively.
 3119         complexLambdaElecB: List of lambda values for electrostatics. The
 3120             values of 0 and 1 imply state A and state B respectively.
 3121         complexLambdaRestraintsA: List of lambda values for restraints. The
 3122             values of 0 and 1 imply state A and state B respectively.
 3123         complexLambdaRestraintsB: List of lambda values for restraints. The
 3124             values of 0 and 1 imply state A and state B respectively.
 3125         complexLambdaVdwA: List of lamda values for van der Waals. The
 3126             values of of 0 and 1 imply state A and state B respectively.
 3127         complexLambdaVdwB: List of lamda values for van der Waals. The
 3128             values of of 0 and 1 imply state A and state B respectively.
 3129 
 3130         Complex output settings:
 3131         
 3132         Parameters controlling simulation output during final phase of
 3133         complex transformation.
 3134 
 3135         complexOutputCheckpointInterval:  Frequency to write the checkpoint
 3136             file.
 3137         complexOutputCheckpointStorageFilename: Checkpoint filename.
 3138         complexOutputForcefieldCache: Filename for caching small molecule
 3139             residue templates.
 3140         complexOutputFilename: Trajectory filename.
 3141         complexOutputIndices: Selection string for selecting coordinates to
 3142             write.
 3143         complexOutputStructure: Topology structure filename.
 3144         complexOutputPositionsWriteFrequency: Frequency for writing
 3145             positions to trajectory file.
 3146         complexOutputVelocitiesWriteFrequency: Frequency for writing
 3147             velocities to trajectory file.
 3148 
 3149         Complex restraint settings:
 3150         
 3151         Parameters to configure Boresch-style restraint between two groups
 3152         of atoms named host  (Hx) and guest (Gx).
 3153 
 3154         complexRestraintKPhiA: Equilibrium force constant for the dihedral
 3155             formed by H2-H1-H0-G0.
 3156         complexRestraintKPhiB: Equilibrium force constant for the dihedral
 3157             formed by H1-H0-G0-G1.
 3158         complexRestraintKPhiC: Equilibrium force constant for the dihedral
 3159             formed by H0-G0-G1-G2.
 3160         complexRestraintKR: Bond spring constant between H0 and G0.
 3161         restraintKThetaA: Spring constant for the angle formed by H1-H0-G0.
 3162         complexRestraintKThetaA: Spring constant for the angle formed by
 3163             H1-H0-G0.
 3164         complexRestraintKThetaB:  Spring constant for the angle formed by
 3165             H0-G0-G1.
 3166         complexRestraintAnchorFindingStrategy: Boresch atom picking strategy
 3167             to use. bonded: pick host atoms that are bonded to each other.
 3168             multi-residue: pick host atoms which can span multiple residues.
 3169         complexRestraintDsspFilter: Apply DSSP filter to the host atoms.
 3170         complexRestraintHostMaxDistance: Maximum distance between any
 3171             host atom and the guest G0 atom.
 3172         complexRestraintHostMinDistance: Minimum distance between any
 3173             host atom and the guest G0 atom
 3174         complexRestraintHostSelection: A valid selection string to
 3175             sub-select the host atoms which will be involved in the
 3176             restraint.
 3177         complexRestraintRmsfCutoff: Cutoff value for filtering atoms by their
 3178             root mean square fluctuation. Atoms with values above this
 3179             cutoff are ignored.
 3180 
 3181         Complex simulation settings:
 3182         
 3183         Parameters controlling simulation during final phase of complex
 3184         transformation.
 3185 
 3186         complexSimulationEarlyTerminationTargetError: Target error for the
 3187             real time analysis measured in kcal/mol. Once the MBAR error of
 3188             the free energy is at or below this value, the simulation will
 3189             be considered complete. The suggested value of 0.12 has shown to
 3190             be effective in both hydration and binding free energy
 3191                 benchmarks.
 3192         complexSimulationEquilibrationLength: Length of the equilibration
 3193             phase. The specified value must be divisible by
 3194             'integratorTimestep'.
 3195         complexSimulationMinimizationSteps: Maximum number of minimization
 3196             steps to perform.
 3197         complexSimulationNReplicas: Number of replicas to use.
 3198         complexSimulationProductionLength: Length of the production phase.
 3199             The specified value must be divisible by 'integratorTimestep'.
 3200         complexSimulationRealTimeAnalysisMinimumTime: Time interval for
 3201             performing analysis of the free energies. At each interval, real
 3202             time analysis data will be written to a yaml file named
 3203             <outputFileName>_real_time_analysis.yaml. The current error
 3204             in the estimate will also be assessed and the simulation will
 3205             be terminated when it drops below
 3206             'complexSimulationEarlyTerminationTargetError'.
 3207         complexSimulationSamplerMethod: Alchemical sampling method to use:
 3208             REPEX (Hamiltonian REPlica EXchange), SAMS (Self-Adjusted
 3209             Mixture Sampling), or Independent (Independently sampled lambda
 3210             windows).
 3211         complexSimulationSamsFlatnessCriteria:Method for assessing when to
 3212             switch to asymptomatically optimal scheme for SAMS.
 3213         complexSimulationsamsGamma0: Initial weight adaptation rate for
 3214             SAMS.
 3215         complexSimulationTimePerIteration: Simulation time between each
 3216             MCMC move attempt 
 3217 
 3218         Complex solvation settings:
 3219 
 3220         Solvation parameters for the system, including the solvent model and
 3221         the solvent padding.
 3222 
 3223         complexSolvationBoxShape: Shape of the periodic solvent box.
 3224         complexSolvationBoxSize:  Lengths of the unit cell for a solvent box.
 3225         complexSolvationSolventModel: Forcefield water model to use during
 3226             solvation and defining the model properties.
 3227         complexSolvationSolventPadding: Minimum distance from any solute
 3228             bounding sphere to the edge of the box.
 3229 
 3230         Engine settings:
 3231         
 3232         Parameters configuring the compute platform used by the OpenMM to
 3233         perform the simulation.
 3234 
 3235         engineComputePlatform: Platform to use for running OpenMM MD
 3236             calculations.
 3237         engineGpuDeviceIndex: Space delimited list of device indices
 3238             to use for running OpenMM MD calculations.
 3239 
 3240         Forcefield settings:
 3241         
 3242         forcefieldConstraints:Constraints  to use.
 3243         forcefields: List of valid forcefield paths for all components
 3244             except small molecules.
 3245         forcefieldHydrogenMass: Mass to be repartitioned to hydrogens
 3246             from neighboring heavy atoms.
 3247         forcefieldNonbondedCutoff: Cutoff for short range nonbonded
 3248             interactions.
 3249         forcefieldNonbondedMethod: Method for treating nonbonded
 3250             interactions.
 3251         forcefieldRigidWater: Use a rigid water model.
 3252         forcefieldSmallMoleculeForcefield: A valid forcefield name to use
 3253             for small molecules.
 3254 
 3255         Integrator settings:
 3256 
 3257         Parameters controlling the LangevinSplittingDynamicsMove integrator
 3258         used for simulation.
 3259 
 3260         integratorBarostatFrequency: Frequency at which volume scaling
 3261             changes should be attempted.
 3262         integratorConstraintTolerance: Tolerance for constraint solver.
 3263         integratorLangevinCollisionRate: Collision frequency.
 3264         integratorNRestartAttempts: Number of attempts to restart from
 3265             Context in case there are NaNs in the energies after
 3266             integration.
 3267         integratorReassignVelocities: Reassign velocities  from the
 3268             Maxwell-Boltzmann distribution at the beginning of each
 3269             Monte Carlo move.
 3270         integratorRemoveCom: Remove the center of mass motion.
 3271         integratorTimestep: Size of the simulation timestep.
 3272 
 3273         Partial charge settings:
 3274         
 3275         Parameters for automatically assigning missing partial charges to
 3276         small molecules, including the partial charge method.
 3277 
 3278         partialChargeNaglModel: Model to use for partial charge assignment.
 3279             A value of None implies the use of the latest available
 3280             production AM1BCC model.
 3281         partialChargeNumberOfConformers: Number of conformers to generate
 3282             as part of the partial charge assignment. A value of None
 3283             implies the use of the existing conformer.
 3284         partialChargeOffToolkitBackend: OpenFF toolkit registry backend to
 3285             use for calculating partial charges.
 3286         partialChargeMethod: Method to use for calculating partial charges.
 3287 
 3288         Solvent equil output settings:
 3289         Solvent equil simulation settings:
 3290         Solvent lambda settings:
 3291         Solvent output settings:
 3292         Solvent restraint settings:
 3293         Solvent simulation settings:
 3294         Solvent solvation settings:
 3295 
 3296         The solvent settings are similar to the complex settings already
 3297         described under various sections for complex. The prefix 'solvent'
 3298         is used for the names of the pramaters instead of the prefix
 3299         'complex.'
 3300 
 3301         Thermo settings:
 3302         
 3303         Thermodynamic parameters, including the temperature and the pressure
 3304         of the system.
 3305 
 3306         thermoPh: Simulation pH
 3307         thermoPressure: Simulation pressure.
 3308         thermoRedoxPotential:Simulation redox potential.
 3309         thermoTemperature: Simulation temperature. 
 3310 
 3311     Arguments:
 3312         ParamsOptionName (str): Command line OpenFE RFE parameters option name.
 3313         ParamsOptionValue (str): Comma delimited list of parameter name and value pairs.
 3314         ParamsDefaultInfo (dict): Default values to override selected parameters.
 3315 
 3316     Returns:
 3317         dictionary: Processed parameter name and value pairs.
 3318 
 3319     """
 3320 
 3321     ParamsInfo = _SetupRelativeBindingFreeEnergySeparatedTopologyDefaultParametersInfo(ParamsOptionName, ParamsOptionValue)
 3322 
 3323     (ValidParamNames, CanonicalParamNamesMap, ParamsOptionValue, ParamsOptionValueWords) = (
 3324         _ValidateAndCanonicalizeParameterNames(ParamsOptionName, ParamsOptionValue, ParamsInfo, ParamsDefaultInfo)
 3325     )
 3326 
 3327     if re.match("^auto$", ParamsOptionValue, re.I):
 3328         _ProcessOptionOpenFERelativeBindingFreeEnergySeparatedTopologyParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 3329         return ParamsInfo
 3330 
 3331     for Index in range(0, len(ParamsOptionValueWords), 2):
 3332         Name = ParamsOptionValueWords[Index].strip()
 3333         Value = ParamsOptionValueWords[Index + 1].strip()
 3334 
 3335         ParamName = CanonicalParamNamesMap[Name.lower()]
 3336         ParamValue = Value
 3337 
 3338         if re.match(
 3339             "^(ProtocolRepeats|IntegratorNRestartAttempts|ComplexEquilSimulationMinimizationSteps|ComplexSimulationMinimizationSteps|ComplexSimulationNReplicas|IntegratorNRestartAttempts|PartialChargeNumberOfConformers|SolventEquilSimulationMinimizationSteps|SolventSimulationMinimizationSteps|SolventSimulationNReplicas)$",
 3340             ParamName,
 3341             re.I,
 3342         ):
 3343             #  Int > 0
 3344             if not MiscUtil.IsInteger(Value):
 3345                 MiscUtil.PrintError(
 3346                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 3347                     % (Value, ParamName, ParamsOptionName)
 3348                 )
 3349             Value = int(Value)
 3350             if Value <= 0:
 3351                 MiscUtil.PrintError(
 3352                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3353                     % (ParamValue, ParamName, ParamsOptionName)
 3354                 )
 3355             ParamValue = Value
 3356         elif re.match(
 3357             "^(IntegratorConstraintTolerance|ComplexSimulationSamsGamma0|ForcefieldHydrogenMass|SolventSimulationSamsGamma0)$",
 3358             ParamName,
 3359             re.I,
 3360         ):
 3361             # float > 0
 3362             if not MiscUtil.IsFloat(Value):
 3363                 MiscUtil.PrintError(
 3364                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3365                     % (Value, ParamName, ParamsOptionName)
 3366                 )
 3367             Value = float(Value)
 3368             if Value <= 0:
 3369                 MiscUtil.PrintError(
 3370                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3371                     % (ParamValue, ParamName, ParamsOptionName)
 3372                 )
 3373             ParamValue = Value
 3374         elif re.match(
 3375             "^(ForcefieldRigidWater|IntegratorReassignVelocities|IntegratorRemoveCom|ComplexRestraintDsspFilter|SolventRestraintCentralAtomsOnly)$",
 3376             ParamName,
 3377             re.I,
 3378         ):
 3379             #  bool
 3380             if not re.match("^(yes|no|true|false)$", Value, re.I):
 3381                 MiscUtil.PrintError(
 3382                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: yes or no'
 3383                     % (Value, Name, ParamsOptionName)
 3384                 )
 3385             ParamValue = True if re.match("^(yes|true)$", Value, re.I) else False
 3386         elif re.match("^ThermoPh$", ParamName, re.I):
 3387             #  float > 0 or None
 3388             if re.match("^None$", Value, re.I):
 3389                 ParamValue = None
 3390             else:
 3391                 if not MiscUtil.IsFloat(Value):
 3392                     MiscUtil.PrintError(
 3393                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3394                         % (Value, ParamName, ParamsOptionName)
 3395                     )
 3396                 Value = float(Value)
 3397                 if Value <= 0:
 3398                     MiscUtil.PrintError(
 3399                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3400                         % (ParamValue, ParamName, ParamsOptionName)
 3401                     )
 3402                 ParamValue = Value
 3403         elif re.match("^ThermoRedoxPotential$", ParamName, re.I):
 3404             if re.match("^None$", Value, re.I):
 3405                 ParamValue = None
 3406             else:
 3407                 if not MiscUtil.IsFloat(Value):
 3408                     MiscUtil.PrintError(
 3409                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3410                         % (Value, ParamName, ParamsOptionName)
 3411                     )
 3412                 Value = float(Value)
 3413                 ParamValue = Value * openff.units.unit.millivolts
 3414         elif re.match("^IntegratorLangevinCollisionRate$", ParamName, re.I):
 3415             if not MiscUtil.IsFloat(Value):
 3416                 MiscUtil.PrintError(
 3417                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3418                     % (Value, ParamName, ParamsOptionName)
 3419                 )
 3420             Value = float(Value)
 3421             if Value <= 0:
 3422                 MiscUtil.PrintError(
 3423                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3424                     % (ParamValue, ParamName, ParamsOptionName)
 3425                 )
 3426             ParamValue = Value / openff.units.unit.picosecond
 3427         elif re.match(
 3428             "^(ComplexLambdaElecA|ComplexLambdaElecB|ComplexLambdaRestraintsA|ComplexLambdaRestraintsB|ComplexLambdaVdwA|ComplexLambdaVdwB|SolventLambdaElecA|SolventLambdaElecB|SolventLambdaRestraintsA|SolventLambdaRestraintsB|SolventLambdaVdwA|SolventLambdaVdwB)$",
 3429             ParamName,
 3430             re.I,
 3431         ):
 3432             # List of float values between 0 and 1...
 3433             Values = Value.split()
 3434             if len(Values) == 0:
 3435                 MiscUtil.PrintError(
 3436                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain a set of space delimited values\n'
 3437                     % (Value, ParamName, ParamsOptionName)
 3438                 )
 3439             for Value in Values:
 3440                 if not MiscUtil.IsFloat(Value):
 3441                     MiscUtil.PrintError(
 3442                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3443                         % (Value, ParamName, ParamsOptionName)
 3444                     )
 3445                 Value = float(Value)
 3446                 if Value < 0.0 or Value > 1.0:
 3447                     MiscUtil.PrintError(
 3448                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not valid value. Supported values: 0.0 to 1.0\n'
 3449                         % (Value, ParamName, ParamsOptionName)
 3450                     )
 3451             Values = [float(Value) for Value in Values]
 3452             ParamValue = Values
 3453         elif re.match("^IntegratorBarostatFrequency$", ParamName, re.I):
 3454             if not MiscUtil.IsFloat(Value):
 3455                 MiscUtil.PrintError(
 3456                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3457                     % (Value, ParamName, ParamsOptionName)
 3458                 )
 3459             Value = float(Value)
 3460             if Value <= 0:
 3461                 MiscUtil.PrintError(
 3462                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3463                     % (ParamValue, ParamName, ParamsOptionName)
 3464                 )
 3465             ParamValue = Value * openff.units.unit.timestep
 3466         elif re.match("^IntegratorLangevinCollisionRate$", ParamName, re.I):
 3467             if not MiscUtil.IsFloat(Value):
 3468                 MiscUtil.PrintError(
 3469                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3470                     % (Value, ParamName, ParamsOptionName)
 3471                 )
 3472             Value = float(Value)
 3473             if Value <= 0:
 3474                 MiscUtil.PrintError(
 3475                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3476                     % (ParamValue, ParamName, ParamsOptionName)
 3477                 )
 3478             ParamValue = Value / openff.units.unit.picosecond
 3479         elif re.match("^IntegratorTimestep$", ParamName, re.I):
 3480             # float > 0 femtosecond
 3481             if not MiscUtil.IsFloat(Value):
 3482                 MiscUtil.PrintError(
 3483                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3484                     % (Value, ParamName, ParamsOptionName)
 3485                 )
 3486             Value = float(Value)
 3487             if Value <= 0:
 3488                 MiscUtil.PrintError(
 3489                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3490                     % (ParamValue, ParamName, ParamsOptionName)
 3491                 )
 3492             ParamValue = Value * openff.units.unit.femtosecond
 3493         elif re.match(
 3494             "^(ComplexEquilOutputTrajectoryWriteInterval|ComplexOutputPositionsWriteFrequency|ComplexSimulationRealTimeAnalysisInterval|ComplexSimulationRealTimeAnalysisMinimumTime|ComplexSimulationTimePerIteration|SolventEquilOutputTrajectoryWriteInterval|SolventOutputPositionsWriteFrequency|SolventSimulationRealTimeAnalysisInterval|SolventSimulationRealTimeAnalysisMinimumTime|SolventSimulationTimePerIteration)$",
 3495             ParamName,
 3496             re.I,
 3497         ):
 3498             #  float > 0 picosecond
 3499             if not MiscUtil.IsFloat(Value):
 3500                 MiscUtil.PrintError(
 3501                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3502                     % (Value, ParamName, ParamsOptionName)
 3503                 )
 3504             Value = float(Value)
 3505             if Value <= 0:
 3506                 MiscUtil.PrintError(
 3507                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3508                     % (ParamValue, ParamName, ParamsOptionName)
 3509                 )
 3510             ParamValue = Value * openff.units.unit.picosecond
 3511         elif re.match(
 3512             "^(ComplexEquilOutputCheckpointInterval|ComplexEquilSimulationEquilibrationLength|ComplexEquilSimulationEquilibrationLengthNVT|ComplexEquilSimulationProductionLength|ComplexOutputCheckpointInterval|ComplexSimulationEquilibrationLength|ComplexSimulationProductionLength|SolventEquilOutputCheckpointInterval|SolventEquilSimulationEquilibrationLength|SolventEquilSimulationEquilibrationLengthNVT|SolventEquilSimulationProductionLength|SolventOutputCheckpointInterval|SolventSimulationEquilibrationLength|SolventSimulationProductionLength)$",
 3513             ParamName,
 3514             re.I,
 3515         ):
 3516             #  float > 0 nanosecond
 3517             if not MiscUtil.IsFloat(Value):
 3518                 MiscUtil.PrintError(
 3519                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3520                     % (Value, ParamName, ParamsOptionName)
 3521                 )
 3522             Value = float(Value)
 3523             if Value <= 0:
 3524                 MiscUtil.PrintError(
 3525                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3526                     % (ParamValue, ParamName, ParamsOptionName)
 3527                 )
 3528             ParamValue = Value * openff.units.unit.nanosecond
 3529         elif re.match(
 3530             "^(ComplexOutputVelocitiesWriteFrequency|SolventOutputVelocitiesWriteFrequency)$", ParamName, re.I
 3531         ):
 3532             #  float > 0 picosecond or none
 3533             if re.match("^None$", Value, re.I):
 3534                 ParamValue = None
 3535             else:
 3536                 if not MiscUtil.IsFloat(Value):
 3537                     MiscUtil.PrintError(
 3538                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3539                         % (Value, ParamName, ParamsOptionName)
 3540                     )
 3541                 Value = float(Value)
 3542                 if Value <= 0:
 3543                     MiscUtil.PrintError(
 3544                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3545                         % (ParamValue, ParamName, ParamsOptionName)
 3546                     )
 3547                 ParamValue = Value * openff.units.unit.picosecond
 3548         elif re.match("^PartialChargeMethod$", ParamName, re.I):
 3549             if not re.match("^(AM1BCC|AM1BCCELF10|Espaloma|NAGL)$", Value, re.I):
 3550                 MiscUtil.PrintError(
 3551                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: AM1BCC, AM1BCCELF10, Espaloma, or NAGL'
 3552                     % (Value, Name, ParamsOptionName)
 3553                 )
 3554             ParamValue = Value.lower()
 3555         elif re.match("^PartialChargeOffToolkitBackend$", ParamName, re.I):
 3556             if not re.match("^(AmberTools|OpenEye|RDKit)$", Value, re.I):
 3557                 MiscUtil.PrintError(
 3558                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: AmberTools, OpenEye, or RDKit'
 3559                     % (Value, Name, ParamsOptionName)
 3560                 )
 3561             ParamValue = Value.lower()
 3562         elif re.match("^(ComplexSolvationBoxShape|SolventSolvationBoxShape)$", ParamName, re.I):
 3563             if not re.match("^(cube|dodecahedron|octahedron)$", Value, re.I):
 3564                 MiscUtil.PrintError(
 3565                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: cube, dodecahedron, or octahedron'
 3566                     % (Value, Name, ParamsOptionName)
 3567                 )
 3568             ParamValue = Value.lower()
 3569         elif re.match("^(ComplexSolvationBoxSize|SolventSolvationBoxSize)$", ParamName, re.I):
 3570             # List of X, Y, Z values...
 3571             if re.match("^None$", Value, re.I):
 3572                 ParamValue = None
 3573             else:
 3574                 Values = Value.split()
 3575                 if len(Values) != 3:
 3576                     MiscUtil.PrintError(
 3577                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain a set of three space delimited values.\n'
 3578                         % (Value, ParamName, ParamsOptionName)
 3579                     )
 3580                 for Value in Values:
 3581                     if not MiscUtil.IsFloat(Value):
 3582                         MiscUtil.PrintError(
 3583                             'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3584                             % (Value, ParamName, ParamsOptionName)
 3585                         )
 3586                 Values = [float(Value) for Value in Values]
 3587                 ParamValue = Values * openff.units.unit.nanometer
 3588         elif re.match("^(ComplexSolvationSolventModel|SolventSolvationSolventModel)$", ParamName, re.I):
 3589             if not re.match("^(tip3p|spce|tip4pew|tip5p)$", Value, re.I):
 3590                 MiscUtil.PrintError(
 3591                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: tip3p, spce, tip4pew, or tip5p'
 3592                     % (Value, Name, ParamsOptionName)
 3593                 )
 3594             ParamValue = Value.lower()
 3595         elif re.match("^EngineComputePlatform$", ParamName, re.I):
 3596             if not re.match("^(CPU|CUDA|OpenCL|Reference)$", Value, re.I):
 3597                 MiscUtil.PrintError(
 3598                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: CPU, CUDA, OpenCL, or Reference'
 3599                     % (Value, Name, ParamsOptionName)
 3600                 )
 3601             ParamValue = Value
 3602         elif re.match(
 3603             "^(ForcefieldNonbondedCutoff|ComplexRestraintHostMaxDistance|ComplexRestraintHostMinDistance|ComplexRestraintRmsfCutoff)$",
 3604             ParamName,
 3605             re.I,
 3606         ):
 3607             #  float > 0 and units nanometer
 3608             if not MiscUtil.IsFloat(Value):
 3609                 MiscUtil.PrintError(
 3610                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3611                     % (Value, ParamName, ParamsOptionName)
 3612                 )
 3613             Value = float(Value)
 3614             if Value <= 0:
 3615                 MiscUtil.PrintError(
 3616                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3617                     % (ParamValue, ParamName, ParamsOptionName)
 3618                 )
 3619             ParamValue = Value * openff.units.unit.nanometer
 3620         elif re.match("^(ComplexSolvationSolventPadding|SolventSolvationSolventPadding)$", ParamName, re.I):
 3621             #  float > 0 and units nanometer or none
 3622             if re.match("^None$", Value, re.I):
 3623                 ParamValue = None
 3624             else:
 3625                 if not MiscUtil.IsFloat(Value):
 3626                     MiscUtil.PrintError(
 3627                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3628                         % (Value, ParamName, ParamsOptionName)
 3629                     )
 3630                 Value = float(Value)
 3631                 if Value <= 0:
 3632                     MiscUtil.PrintError(
 3633                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3634                         % (ParamValue, ParamName, ParamsOptionName)
 3635                     )
 3636                 ParamValue = Value * openff.units.unit.nanometer
 3637         elif re.match("^EngineGpuDeviceIndex$", ParamName, re.I):
 3638             #  Comma delimited string values...
 3639             DeviceIndices = Value.split()
 3640             if len(DeviceIndices) == 0:
 3641                 MiscUtil.PrintError(
 3642                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain space delimited list of device indices.\n'
 3643                     % (Value, ParamName, ParamsOptionName)
 3644                 )
 3645             for DeviceIndex in DeviceIndices:
 3646                 if not MiscUtil.IsInteger(DeviceIndex):
 3647                     MiscUtil.PrintError(
 3648                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 3649                         % (DeviceIndex, ParamName, ParamsOptionName)
 3650                     )
 3651                 DeviceIndices = [int(DeviceIndex) for DeviceIndex in DeviceIndices]
 3652             ParamValue = DeviceIndices
 3653         elif re.match("^(ForcefieldConstraints)$", ParamName, re.I):
 3654             if not re.match("^(HBonds|AllBonds|HAngles|None)$", Value, re.I):
 3655                 MiscUtil.PrintError(
 3656                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: HBonds, AllBonds, HAngles, or None'
 3657                     % (Value, Name, ParamsOptionName)
 3658                 )
 3659             ParamValue = None if re.match("^None$", Value, re.I) else Value.lower()
 3660         elif re.match("^(Forcefields)$", ParamName, re.I):
 3661             #  List of string values.....
 3662             Values = Value.split()
 3663             if len(Values) == 0:
 3664                 MiscUtil.PrintError(
 3665                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain a space delimited list of values..\n'
 3666                     % (Value, ParamName, ParamsOptionName)
 3667                 )
 3668             ParamValue = Values
 3669         elif re.match("^(ForcefieldNonbondedMethod)$", ParamName, re.I):
 3670             if not re.match("^(PME|NoCutoff)$", Value, re.I):
 3671                 MiscUtil.PrintError(
 3672                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is may be a valid OpenFE value. Supported values: PME or NoCutoff'
 3673                     % (Value, Name, ParamsOptionName)
 3674                 )
 3675             ParamValue = Value.lower()
 3676         elif re.match(
 3677             "^(ComplexSimulationEarlyTerminationTargetError|SolventSimulationEarlyTerminationTargetError)$",
 3678             ParamName,
 3679             re.I,
 3680         ):
 3681             # float >= 0 units: kilocalorie_per_mole
 3682             if not MiscUtil.IsFloat(Value):
 3683                 MiscUtil.PrintError(
 3684                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3685                     % (Value, ParamName, ParamsOptionName)
 3686                 )
 3687             Value = float(Value)
 3688             if Value < 0:
 3689                 MiscUtil.PrintError(
 3690                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3691                     % (ParamValue, ParamName, ParamsOptionName)
 3692                 )
 3693             ParamValue = Value * openff.units.unit.kilocalorie_per_mole
 3694         elif re.match(
 3695             "^(ComplexRestraintKPhiA|ComplexRestraintKPhiB|ComplexRestraintKPhiC|ComplexRestraintKThetaA|ComplexRestraintKThetaB)$", ParamName, re.I
 3696         ):
 3697             # float > 0 units: kilojoule_per_mole / radian ** 2
 3698             if not MiscUtil.IsFloat(Value):
 3699                 MiscUtil.PrintError(
 3700                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3701                     % (Value, ParamName, ParamsOptionName)
 3702                 )
 3703             Value = float(Value)
 3704             if Value <= 0:
 3705                 MiscUtil.PrintError(
 3706                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3707                     % (ParamValue, ParamName, ParamsOptionName)
 3708                 )
 3709             ParamValue = Value * openff.units.unit.kilojoule_per_mole / openff.units.unit.radian**2
 3710         elif re.match("^(ComplexRestraintKR)$", ParamName, re.I):
 3711             # float > 0 units: kilojoule_per_mole / nanometer ** 2
 3712             if not MiscUtil.IsFloat(Value):
 3713                 MiscUtil.PrintError(
 3714                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3715                     % (Value, ParamName, ParamsOptionName)
 3716                 )
 3717             Value = float(Value)
 3718             if Value <= 0:
 3719                 MiscUtil.PrintError(
 3720                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3721                     % (ParamValue, ParamName, ParamsOptionName)
 3722                 )
 3723             ParamValue = Value * openff.units.unit.kilojoule_per_mole / openff.units.unit.nanometer**2
 3724         elif re.match("^ComplexRestraintAnchorFindingStrategy$", ParamName, re.I):
 3725             if not re.match("^(multi-residue|bonded)$", Value, re.I):
 3726                 MiscUtil.PrintError(
 3727                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: multi-residue or bonded'
 3728                     % (Value, Name, ParamsOptionName)
 3729                 )
 3730             ParamValue = Value
 3731         elif re.match("^(solventRestraintSpringConstant)$", ParamName, re.I):
 3732             # float > 0 units: kilojoule_per_mole / nanometer ** 2
 3733             if not MiscUtil.IsFloat(Value):
 3734                 MiscUtil.PrintError(
 3735                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3736                     % (Value, ParamName, ParamsOptionName)
 3737                 )
 3738             Value = float(Value)
 3739             if Value <= 0:
 3740                 MiscUtil.PrintError(
 3741                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3742                     % (ParamValue, ParamName, ParamsOptionName)
 3743                 )
 3744             ParamValue = Value * openff.units.unit.kilojoule_per_mole / openff.units.unit.nanometer**2
 3745         elif re.match("^(ComplexSimulationSamplerMethod|SolventSimulationSamplerMethod)$", ParamName, re.I):
 3746             if not re.match("^(repex|sams|independent)$", Value, re.I):
 3747                 MiscUtil.PrintError(
 3748                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: repex, sams, or independent'
 3749                     % (Value, Name, ParamsOptionName)
 3750                 )
 3751             ParamValue = Value.lower()
 3752         elif re.match(
 3753             "^(ComplexSimulationSamsFlatnessCriteria|SolventSimulationSamsFlatnessCriteria)$", ParamName, re.I
 3754         ):
 3755             if not re.match("^(logz-flatness|minimum-visits|histogram-flatness)$", Value, re.I):
 3756                 MiscUtil.PrintError(
 3757                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: logz-flatness, minimum-visits, or histogram-flatness'
 3758                     % (Value, Name, ParamsOptionName)
 3759                 )
 3760             ParamValue = Value.lower()
 3761         elif re.match("^ThermoPressure$", ParamName, re.I):
 3762             #  float > 0 and units standard_atmosphere
 3763             if not MiscUtil.IsFloat(Value):
 3764                 MiscUtil.PrintError(
 3765                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3766                     % (Value, ParamName, ParamsOptionName)
 3767                 )
 3768             Value = float(Value)
 3769             if Value <= 0:
 3770                 MiscUtil.PrintError(
 3771                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 3772                     % (ParamValue, ParamName, ParamsOptionName)
 3773                 )
 3774             ParamValue = Value * openff.units.unit.bar
 3775         elif re.match("^ThermoTemperature$", ParamName, re.I):
 3776             # float >= 0 and units kelvin
 3777             if not MiscUtil.IsFloat(Value):
 3778                 MiscUtil.PrintError(
 3779                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 3780                     % (Value, ParamName, ParamsOptionName)
 3781                 )
 3782             Value = float(Value)
 3783             if Value < 0:
 3784                 MiscUtil.PrintError(
 3785                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: >= 0\n'
 3786                     % (ParamValue, ParamName, ParamsOptionName)
 3787                 )
 3788             ParamValue = Value * openff.units.unit.kelvin
 3789         else:
 3790             # Str or None...
 3791             ParamValue = None if re.match("^None$", Value, re.I) else Value
 3792 
 3793         # Set value...
 3794         ParamsInfo[ParamName] = ParamValue
 3795 
 3796     # Handle parameters with possible auto values...
 3797     _ProcessOptionOpenFERelativeBindingFreeEnergySeparatedTopologyParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 3798 
 3799     return ParamsInfo
 3800 
 3801 def _ProcessOptionOpenFERelativeBindingFreeEnergySeparatedTopologyParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 3802     """Process parameters with possible auto values and perform validation."""
 3803 
 3804     for NamePrefix in ["Complex", "Solvent"]:
 3805         ParamName1 = "%sSolvationBoxSize" % NamePrefix
 3806         ParamValue1 = ParamsInfo[ParamName1]
 3807         ParamName2 = "%sSolvationSolventPadding" % NamePrefix
 3808         ParamValue2 = ParamsInfo[ParamName2]
 3809         if ParamsInfo[ParamName1] is not None and ParamsInfo[ParamName2] is not None:
 3810             MiscUtil.PrintError(
 3811                 'The parameter values, %s and %s, specified for parameter names, %s and %s, using "%s" option is not a valid value. You must specify only one of these values.\n'
 3812                 % (ParamValue1, ParamValue2, ParamName1, ParamName2, ParamsOptionName)
 3813             )
 3814 
 3815     for NamePrefix in ["Complex", "Solvent"]:
 3816         ParamNames = []
 3817         ParamValuesCount = []
 3818         for ParamType in ["LambdaElecA", "LambdaElecB", "LambdaRestraintsA", "LambdaRestraintsA", "LambdaVdwA", "LambdaVdwB"]:
 3819             ParamName = "%s%s" % (NamePrefix, ParamType)
 3820             ParamValueCount = len(ParamsInfo[ParamName])
 3821             ParamNames.append(ParamName)
 3822             ParamValuesCount.append(ParamValueCount)
 3823         
 3824         for Index in range(1, len(ParamNames)):
 3825             if ParamValuesCount[Index] != ParamValuesCount[0]:
 3826                 ParamValuesCount = ["%s" % Value for Value in ParamValuesCount]
 3827                 MiscUtil.PrintError("The number of values - %s - specified for parameter names - %s - using \"%s\" option are not valid. You must specify same number of values for these parameters." % (",".join(ParamValuesCount), ",".join(ParamNames), ParamsOptionName))
 3828         
 3829     _ProcessPartialChargeMethodRelativeBindingFreeEnergySeparatedTopologyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 3830     _ProcessPartialChargeNaglRelativeBindingFreeEnergySeparatedTopologyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 3831 
 3832 def _ProcessPartialChargeMethodRelativeBindingFreeEnergySeparatedTopologyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 3833     """Process  PartialChargeMethod RBFE paramater."""
 3834 
 3835     _ProcessPartialChargeMethodFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 3836 
 3837 
 3838 def _ProcessPartialChargeNaglRelativeBindingFreeEnergySeparatedTopologyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 3839     """Process  PartialChargeNaglModel RBFE paramater."""
 3840 
 3841     _ProcessPartialChargeNaglFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 3842 
 3843 def _SetupRelativeBindingFreeEnergySeparatedTopologyDefaultParametersInfo(ParamsOptionName, ParamsOptionValue):
 3844     """Setup RBFE default parameters information using the current RBFE settings."""
 3845 
 3846     ParamsInfo = {}
 3847 
 3848     from openfe.protocols.openmm_septop import SepTopProtocol
 3849 
 3850     RBFESettings = SepTopProtocol.default_settings()
 3851     RBFEParametersMap = _SetupMapForRelativeBindingFreeEnergySeparatedTopologyParameters()
 3852 
 3853     for ParamName in RBFEParametersMap.keys():
 3854         RBFEParamGroupName, RBFEParamName = RBFEParametersMap[ParamName]
 3855         if RBFEParamGroupName is None:
 3856             if hasattr(RBFESettings, RBFEParamName):
 3857                 ParamsInfo[ParamName] = getattr(RBFESettings, RBFEParamName)
 3858             else:
 3859                 MiscUtil.PrintInfo(
 3860                     'The OpenFE RBFE settings name, %s, corresponding to RBFE parameter name, %s, specified using option "%s" is not available. Ignoring parameter...'
 3861                     % (RBFEParamName, ParamName, ParamsOptionName)
 3862                 )
 3863         else:
 3864             RBFEParamGroupSettings = (
 3865                 getattr(RBFESettings, RBFEParamGroupName) if hasattr(RBFESettings, RBFEParamGroupName) else None
 3866             )
 3867             if RBFEParamGroupSettings is not None and hasattr(RBFEParamGroupSettings, RBFEParamName):
 3868                 ParamsInfo[ParamName] = getattr(RBFEParamGroupSettings, RBFEParamName)
 3869             else:
 3870                 MiscUtil.PrintInfo(
 3871                     'The OpenFE RBFE parameter name, %s, for settings, %s, corresponding to RBFE parameter name, %s, specified using option "%s" is not available. Ignoring parameter...'
 3872                     % (RBFEParamName, RBFEParamGroupName, ParamName, ParamsOptionName)
 3873                 )
 3874 
 3875     return ParamsInfo
 3876 
 3877 
 3878 def _SetupMapForRelativeBindingFreeEnergySeparatedTopologyParameters():
 3879     """Map relative free energy option paramater names to OpenFE relative
 3880     binding free energy separated topologies settings.
 3881     """
 3882 
 3883     RBFEParametersMap = {
 3884         "ProtocolRepeats": [None, "protocol_repeats"],
 3885         
 3886         "ComplexEquilOutputCheckpointInterval": ["complex_equil_output_settings", "checkpoint_interval"],
 3887         "ComplexEquilOutputCheckpointStorageFilename": ["complex_equil_output_settings", "checkpoint_storage_filename"],
 3888         "ComplexEquilOutputEquilNPTStructure": ["complex_equil_output_settings", "equil_npt_structure"],
 3889         "ComplexEquilOutputEquilNVTstructure": ["complex_equil_output_settings", "equil_nvt_structure"],
 3890         "ComplexEquilOutputForcefieldCache": ["complex_equil_output_settings", "forcefield_cache"],
 3891         "ComplexEquilOutputLogOutput": ["complex_equil_output_settings", "log_output"],
 3892         "ComplexEquilOutputMinimizedStructure": ["complex_equil_output_settings", "minimized_structure"],
 3893         "ComplexEquilOutputIndices": ["complex_equil_output_settings", "output_indices"],
 3894         "ComplexEquilOutputPreminimizedStructure": ["complex_equil_output_settings", "preminimized_structure"],
 3895         "ComplexEquilOutputProductionTrajectoryFilename": ["complex_equil_output_settings", "production_trajectory_filename",],
 3896         "ComplexEquilOutputTrajectoryWriteInterval": ["complex_equil_output_settings", "trajectory_write_interval"],
 3897         
 3898         "ComplexEquilSimulationEquilibrationLength": ["complex_equil_simulation_settings", "equilibration_length"],
 3899         "ComplexEquilSimulationEquilibrationLengthNVT": ["complex_equil_simulation_settings", "equilibration_length_nvt",
 3900         ],
 3901         "ComplexEquilSimulationMinimizationSteps": ["complex_equil_simulation_settings", "minimization_steps"],
 3902         "ComplexEquilSimulationProductionLength": ["complex_equil_simulation_settings", "production_length"],
 3903         
 3904         "ComplexLambdaElecA": ["complex_lambda_settings", "lambda_elec_A"],
 3905         "ComplexLambdaElecB": ["complex_lambda_settings", "lambda_elec_B"],
 3906         "ComplexLambdaRestraintsA": ["complex_lambda_settings", "lambda_restraints_A"],
 3907         "ComplexLambdaRestraintsB": ["complex_lambda_settings", "lambda_restraints_B"],
 3908         "ComplexLambdaVdwA": ["complex_lambda_settings", "lambda_vdw_A"],
 3909         "ComplexLambdaVdwB": ["complex_lambda_settings", "lambda_vdw_B"],
 3910         
 3911         "ComplexOutputCheckpointInterval": ["complex_output_settings", "checkpoint_interval"],
 3912         "ComplexOutputCheckpointStorageFilename": ["complex_output_settings", "checkpoint_storage_filename"],
 3913         "ComplexOutputForcefieldCache": ["complex_output_settings", "forcefield_cache"],
 3914         "ComplexOutputFilename": ["complex_output_settings", "output_filename"],
 3915         "ComplexOutputIndices": ["complex_output_settings", "output_indices"],
 3916         "ComplexOutputStructure": ["complex_output_settings", "output_structure"],
 3917         "ComplexOutputPositionsWriteFrequency": ["complex_output_settings", "positions_write_frequency"],
 3918         "ComplexOutputVelocitiesWriteFrequency": ["complex_output_settings", "velocities_write_frequency"],
 3919         
 3920         "ComplexRestraintKPhiA": ["complex_restraint_settings", "K_phiA"],
 3921         "ComplexRestraintKPhiB": ["complex_restraint_settings", "K_phiB"],
 3922         "ComplexRestraintKPhiC": ["complex_restraint_settings", "K_phiC"],
 3923         "complexRestraintKR": ["complex_restraint_settings", "K_r"],
 3924         "ComplexRestraintKThetaA": ["complex_restraint_settings", "K_thetaA"],
 3925         "ComplexRestraintKThetaB": ["complex_restraint_settings", "K_thetaB"],
 3926         "ComplexRestraintAnchorFindingStrategy": ["complex_restraint_settings", "anchor_finding_strategy"],
 3927         "ComplexRestraintDsspFilter": ["complex_restraint_settings", "dssp_filter"],
 3928         "ComplexRestraintHostMaxDistance": ["complex_restraint_settings", "host_max_distance"],
 3929         "ComplexRestraintHostMinDistance": ["complex_restraint_settings", "host_min_distance"],
 3930         "ComplexRestraintHostSelection": ["complex_restraint_settings", "host_selection"],
 3931         "ComplexRestraintRmsfCutoff": ["complex_restraint_settings", "rmsf_cutoff"],
 3932         
 3933         "ComplexSimulationEarlyTerminationTargetError": ["complex_simulation_settings", "early_termination_target_error",],
 3934         "ComplexSimulationEquilibrationLength": ["complex_simulation_settings", "equilibration_length"],
 3935         "ComplexSimulationMinimizationSteps": ["complex_simulation_settings", "minimization_steps"],
 3936         "ComplexSimulationNReplicas": ["complex_simulation_settings", "n_replicas"],
 3937         "ComplexSimulationProductionLength": ["complex_simulation_settings", "production_length"],
 3938         "ComplexSimulationRealTimeAnalysisInterval": ["complex_simulation_settings", "real_time_analysis_interval"],
 3939         "ComplexSimulationRealTimeAnalysisMinimumTime": ["complex_simulation_settings", "real_time_analysis_minimum_time",],
 3940         "ComplexSimulationSamplerMethod": ["complex_simulation_settings", "sampler_method"],
 3941         "ComplexSimulationSamsFlatnessCriteria": ["complex_simulation_settings", "sams_flatness_criteria"],
 3942         "ComplexSimulationSamsGamma0": ["complex_simulation_settings", "sams_gamma0"],
 3943         "ComplexSimulationTimePerIteration": ["complex_simulation_settings", "time_per_iteration"],
 3944         
 3945         "ComplexSolvationBoxShape": ["complex_solvation_settings", "box_shape"],
 3946         "ComplexSolvationBoxSize": ["complex_solvation_settings", "box_size"],
 3947         "ComplexSolvationSolventModel": ["complex_solvation_settings", "solvent_model"],
 3948         "ComplexSolvationSolventPadding": ["complex_solvation_settings", "solvent_padding"],
 3949         
 3950         "EngineComputePlatform": ["engine_settings", "compute_platform"],
 3951         "EngineGpuDeviceIndex": ["engine_settings", "gpu_device_index"],
 3952         
 3953         "ForcefieldConstraints": ["forcefield_settings", "constraints"],
 3954         "Forcefields": ["forcefield_settings", "forcefields"],
 3955         "ForcefieldHydrogenMass": ["forcefield_settings", "hydrogen_mass"],
 3956         "ForcefieldNonbondedCutoff": ["forcefield_settings", "nonbonded_cutoff"],
 3957         "ForcefieldNonbondedMethod": ["forcefield_settings", "nonbonded_method"],
 3958         "ForcefieldRigidWater": ["forcefield_settings", "rigid_water"],
 3959         "ForcefieldSmallMoleculeForcefield": ["forcefield_settings", "small_molecule_forcefield"],
 3960         
 3961         "IntegratorBarostatFrequency": ["integrator_settings", "barostat_frequency"],
 3962         "IntegratorConstraintTolerance": ["integrator_settings", "constraint_tolerance"],
 3963         "IntegratorLangevinCollisionRate": ["integrator_settings", "langevin_collision_rate"],
 3964         "IntegratorNRestartAttempts": ["integrator_settings", "n_restart_attempts"],
 3965         "IntegratorReassignVelocities": ["integrator_settings", "reassign_velocities"],
 3966         "IntegratorRemoveCom": ["integrator_settings", "remove_com"],
 3967         "IntegratorTimestep": ["integrator_settings", "timestep"],
 3968         
 3969         "PartialChargeNaglModel": ["partial_charge_settings", "nagl_model"],
 3970         "PartialChargeNumberOfConformers": ["partial_charge_settings", "number_of_conformers"],
 3971         "PartialChargeOffToolkitBackend": ["partial_charge_settings", "off_toolkit_backend"],
 3972         "PartialChargeMethod": ["partial_charge_settings", "partial_charge_method"],
 3973         
 3974         "SolventEquilOutputCheckpointInterval": ["solvent_equil_output_settings", "checkpoint_interval"],
 3975         "SolventEquilOutputCheckpointStorageFilename": ["solvent_equil_output_settings", "checkpoint_storage_filename"],
 3976         "SolventEquilOutputEquilNPTStructure": ["solvent_equil_output_settings", "equil_npt_structure"],
 3977         "SolventEquilOutputEquilNVTstructure": ["solvent_equil_output_settings", "equil_nvt_structure"],
 3978         "SolventEquilOutputForcefieldCache": ["solvent_equil_output_settings", "forcefield_cache"],
 3979         "SolventEquilOutputLogOutput": ["solvent_equil_output_settings", "log_output"],
 3980         "SolventEquilOutputMinimizedStructure": ["solvent_equil_output_settings", "minimized_structure"],
 3981         "SolventEquilOutputIndices": ["solvent_equil_output_settings", "output_indices"],
 3982         "SolventEquilOutputPreminimizedStructure": ["solvent_equil_output_settings", "preminimized_structure"],
 3983         "SolventEquilOutputProductionTrajectoryFilename": ["solvent_equil_output_settings", "production_trajectory_filename",],
 3984         "SolventEquilOutputTrajectoryWriteInterval": ["solvent_equil_output_settings", "trajectory_write_interval"],
 3985         
 3986         "SolventEquilSimulationEquilibrationLength": ["solvent_equil_simulation_settings", "equilibration_length"],
 3987         "SolventEquilSimulationEquilibrationLengthNVT": ["solvent_equil_simulation_settings", "equilibration_length_nvt",],
 3988         "SolventEquilSimulationMinimizationSteps": ["solvent_equil_simulation_settings", "minimization_steps"],
 3989         "SolventEquilSimulationProductionLength": ["solvent_equil_simulation_settings", "production_length"],
 3990         
 3991         "SolventLambdaElecA": ["solvent_lambda_settings", "lambda_elec_A"],
 3992         "SolventLambdaElecB": ["solvent_lambda_settings", "lambda_elec_B"],
 3993         "SolventLambdaRestraintsA": ["solvent_lambda_settings", "lambda_restraints_A"],
 3994         "SolventLambdaRestraintsB": ["solvent_lambda_settings", "lambda_restraints_B"],
 3995         "SolventLambdaVdwA": ["solvent_lambda_settings", "lambda_vdw_A"],
 3996         "SolventLambdaVdwB": ["solvent_lambda_settings", "lambda_vdw_B"],
 3997         
 3998         "SolventOutputCheckpointInterval": ["solvent_output_settings", "checkpoint_interval"],
 3999         "SolventOutputCheckpointStorageFilename": ["solvent_output_settings", "checkpoint_storage_filename"],
 4000         "SolventOutputForcefieldCache": ["solvent_output_settings", "forcefield_cache"],
 4001         "SolventOutputFilename": ["solvent_output_settings", "output_filename"],
 4002         "SolventOutputIndices": ["solvent_output_settings", "output_indices"],
 4003         "SolventOutputStructure": ["solvent_output_settings", "output_structure"],
 4004         "SolventOutputPositionsWriteFrequency": ["solvent_output_settings", "positions_write_frequency"],
 4005         "SolventOutputVelocitiesWriteFrequency": ["solvent_output_settings", "velocities_write_frequency"],
 4006         
 4007         "SolventRestraintCentralAtomsOnly": ["solvent_restraint_settings", "central_atoms_only"],
 4008         "SolventRestraintSpringConstant": ["solvent_restraint_settings", "spring_constant"],
 4009         
 4010         "SolventSimulationEarlyTerminationTargetError": ["solvent_simulation_settings", "early_termination_target_error",],
 4011         "SolventSimulationEquilibrationLength": ["solvent_simulation_settings", "equilibration_length"],
 4012         "SolventSimulationMinimizationSteps": ["solvent_simulation_settings", "minimization_steps"],
 4013         "SolventSimulationNReplicas": ["solvent_simulation_settings", "n_replicas"],
 4014         "SolventSimulationProductionLength": ["solvent_simulation_settings", "production_length"],
 4015         "SolventSimulationRealTimeAnalysisInterval": ["solvent_simulation_settings", "real_time_analysis_interval"],
 4016         "SolventSimulationRealTimeAnalysisMinimumTime": ["solvent_simulation_settings", "real_time_analysis_minimum_time",],
 4017         "SolventSimulationSamplerMethod": ["solvent_simulation_settings", "sampler_method"],
 4018         "SolventSimulationSamsFlatnessCriteria": ["solvent_simulation_settings", "sams_flatness_criteria"],
 4019         "SolventSimulationSamsGamma0": ["solvent_simulation_settings", "sams_gamma0"],
 4020         "SolventSimulationTimePerIteration": ["solvent_simulation_settings", "time_per_iteration"],
 4021         
 4022         "SolventSolvationBoxShape": ["solvent_solvation_settings", "box_shape"],
 4023         "SolventSolvationBoxSize": ["solvent_solvation_settings", "box_size"],
 4024         "SolventSolvationSolventModel": ["solvent_solvation_settings", "solvent_model"],
 4025         "SolventSolvationSolventPadding": ["solvent_solvation_settings", "solvent_padding"],
 4026         
 4027         "ThermoPh": ["thermo_settings", "ph"],
 4028         "ThermoPressure": ["thermo_settings", "pressure"],
 4029         "ThermoRedoxPotential": ["thermo_settings", "redox_potential"],
 4030         "ThermoTemperature": ["thermo_settings", "temperature"],
 4031     }
 4032 
 4033     return RBFEParametersMap
 4034 
 4035 
 4036 def SetupRelativeFreeEnergySeparatedTopologySettings(ParamsOptionName, ParamsInfo):
 4037     """Setup relative binding free energy protocol settings to calculate RBFE using separated
 4038     topology.
 4039 
 4040     The ParamsInfo is a comma delimited list of parameter name and value pairs
 4041     returned by ProcessOptionOpenFERelatibveBindingFreeEnergySeparatedTopologyParameters().
 4042 
 4043     Arguments:
 4044         ParamsOptionName (str): Command line OpenFE RFE parameters option name.
 4045         ParamsInfo (dict): Parameter name and value pairs.
 4046 
 4047     Returns:
 4048         object: OpenFE SepTopProtocol settings object.
 4049 
 4050     """
 4051 
 4052     from openfe.protocols.openmm_septop import SepTopProtocol
 4053 
 4054     RBFESettings = SepTopProtocol.default_settings()
 4055     RBFEParametersMap = _SetupMapForRelativeBindingFreeEnergySeparatedTopologyParameters()
 4056 
 4057     _UpdateOpenFESettings("RBFESepTop", ParamsOptionName, ParamsInfo, RBFESettings, RBFEParametersMap)
 4058 
 4059     return RBFESettings
 4060 
 4061 
 4062 def SetupAbsoluteHydrationFreeEnergySettings(ParamsOptionName, ParamsInfo):
 4063     """Setup absolute hydration free energy protocol settings to calculate AHFE.
 4064 
 4065     The ParamsInfo is a comma delimited list of parameter name and value pairs
 4066     returned by ProcessOptionOpenFEAbsoluteHydrationFreeEnergyParameters().
 4067 
 4068     Arguments:
 4069         ParamsOptionName (str): Command line OpenFE RFE parameters option name.
 4070         ParamsInfo (dict): Parameter name and value pairs.
 4071 
 4072     Returns:
 4073         object: OpenFE AbsoluteSolvationProtocol settings object.
 4074 
 4075     """
 4076 
 4077     AHFESettings = AbsoluteSolvationProtocol.default_settings()
 4078     AHFEParametersMap = _SetupMapForAbsoluteHydrationFreeEnergyParameters()
 4079 
 4080     _UpdateOpenFESettings("AHFE", ParamsOptionName, ParamsInfo, AHFESettings, AHFEParametersMap)
 4081 
 4082     return AHFESettings
 4083 
 4084 
 4085 def ProcessOptionOpenFEAbsoluteHydrationFreeEnergyParameters(
 4086     ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo=None
 4087 ):
 4088     """Process parameters for AHFE parameters option and return a map
 4089     containing processed parameter names and values.
 4090 
 4091     The ParamsOptionValue is a comma delimited list of parameter name and value pairs
 4092     to setup AHFE calculations.
 4093 
 4094     The default values are automatically updated to match settings provided by
 4095     OpenFE module AbsoluteSolvationProtocol.
 4096 
 4097     You must specify valid OpenFE values for these parameters. An extensive
 4098     validation is not performed.
 4099 
 4100     The supported parameter names along with their default and possible
 4101     values are shown below:
 4102 
 4103         protocolRepeats, 3
 4104 
 4105         Integrator settings:
 4106 
 4107         integratorBarostatFrequency, 25.0 * timestep  [ The specified value
 4108             is a multiple of integratorTimestep. ]
 4109         integratorConstraintTolerance, 1e-06
 4110         integratorLangevinCollisionRate, 1.0  [ Units: 1 / picosecond ]
 4111         integratorNRestartAttempts, 20
 4112         integratorReassignVelocities, no  [ Possible values: yes or no ]
 4113         integratorRemoveCom, no  [ Possible values: yes or no ]
 4114         integratorTimestep, 4.0 [ Units: femtosecond ]
 4115 
 4116         Lambda settings:
 4117 
 4118         lambdaElec, 0.0 0.25 0.5 0.75 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
 4119             1.0  [ Possible values: A space delimited list of values
 4120             between 0.0 and 1.0 ]
 4121         lambdaRestraints, 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
 4122             0.0 0.0  [ Possible values: A space delimited list of values
 4123             between 0.0 and 1.0 ]
 4124         lambdaVdw, [0.0 0.0 0.0 0.0 0.0 0.12 0.24 0.36 0.48 0.6 0.7 0.77
 4125             0.85 1.0  [ Possible values: A space delimited list of values
 4126             between 0.0 and 1.0 ]
 4127 
 4128         Partial charge settings:
 4129 
 4130         partialChargeNaglModel, None  [ Default: Production AM1BCC model for
 4131             NAGL; Possible value: Any valid name. ]
 4132         partialChargeNumberOfConformers, None  [ Possible value: > 0 ]
 4133         partialChargeOffToolkitBackend, AmberTools  [ Possible values:
 4134             AmberTools or RDKit ]
 4135         partialChargeMethod, AM1BCC  [ Possble values: AM1BCC, Espaloma,
 4136             or NAGL ]
 4137 
 4138         Solvation settings:
 4139 
 4140         solvationBoxShape, dodecahedron  [  Possible values: cube,,
 4141             dodecahedron, or octahedron ]
 4142         solvationBoxSize, None  [ Possible value: A triplet of space
 4143             X Y Z values; Units: nanometer ]
 4144         solvationSolventModel, tip3p  [ Possible values: tip3p, spce, tip4pew,
 4145             or tip5p ]
 4146         solvationSolventPadding, 1.5  [ Units: nanometer ]
 4147 
 4148         Solvent engine settings:
 4149 
 4150         solventEngineComputePlatform, CPU  [ Possible values: CPU, CUDA,
 4151             OpenCL, or Reference ]
 4152         solventEngineGpuDeviceIndex, None [ Possible values: 0, 0 1, etc. ]
 4153 
 4154         Solvent equil output settings:
 4155 
 4156         solventEquilOutputCheckpointInterval, 1.0  [ Units: nanosecond ]
 4157         solventEquilOutputCheckpointStorageFilename, checkpoint.chk
 4158         solventEquilOutputNPTStructure, equil_npt_structure.pdb
 4159         solventEquilOutputNVTStructure, equil_nvt_structure.pdb
 4160         solventEquilOutputForcefieldCache, db.json
 4161         solventEquilOutputLogOutput, equil_simulation.log
 4162         solventEquilOutputMinimizedStructure, minimized.pdb
 4163         solventEquilOutputIndices, not water   [ Possible value: Any valid
 4164             selection. ]
 4165         solventEquilOutputPreminimizedStructure, system.pdb
 4166         solventEquilOutputProductionTrajectoryFilename, production_equil.xtc
 4167         solventEquilOutputTrajectoryWriteInterval, 20.0  [ Units: picosecond ]
 4168 
 4169         Solvent equil simulation settings:
 4170 
 4171         solventEquilSimulationEquilLength, 0.2  [ Units: nanosecond ]
 4172         solventEquilSimulationEquiLengthNVT, 0.1  [ Units: nanosecond ]
 4173         solventEquilSimulationMinimizationSteps,5000
 4174         solventEquilSimulationProductionLength,0.5  [ Units: nanosecond ]
 4175 
 4176         Solvent forcefield settings:
 4177 
 4178         solventForcefieldConstraints, HBonds  [ Possible values: HBonds,
 4179             AllBonds, or HAngles ]
 4180         solventForcefields, amber/ff14SB.xml, amber/tip3p_standard.xml
 4181             amber/tip3p_HFE_multivalent.xml amber/phosaa10.xml
 4182             [ Possible values: A space delimited list of valid names. ]
 4183         solventForcefieldHydrogenMass, 3.0  [ Units: amu ]
 4184         solventForcefieldNonbondedCutoff, 0.9   [ Units: nanometer ]
 4185         solventForcefieldNonbondedMethod, PME  [ Possible values: PME or
 4186             NoCutoff ]
 4187         solventForcefieldRigidWater, yes,  [ Possible values: yes or no ]
 4188         solventForcefieldSmallMoleculeForcefield, openff-2.1.1  [ Possible
 4189             value: A valid forcefield name. ]
 4190 
 4191         Solvent output settings:
 4192 
 4193         solventOutputCheckpointInterval, 1.0  [ Units: nanosecond ]
 4194         solventOutputCheckpointStorageFilename, solvent_checkpoint.nc
 4195         solventOutputForcefieldCache, db.json
 4196         solventOutputFilename, solvent.nc
 4197         solventOutputIndices, not water   [ Possible value: Any valid
 4198             selection. ]
 4199         solventOutputStructure, hybrid_system.pdb
 4200         solventOutputPositionsWriteFrequency, 100.0 [ Units: picosecond ]
 4201         solventOutputVelocitiesWriteFrequency, None  [ Possible
 4202             values: > 0; Units: picosecond ]
 4203 
 4204         Solvent simulation settings:
 4205 
 4206         solventSimulationEarlyTerminationTargetError, 0.0  [ Units:
 4207             kilocalorie_per_mole ]
 4208         solventSimulationEquilibrationLength, 1.0  [ Units: nanosecond ]
 4209         solventSimulationMinimizationSteps, 5000
 4210         solventSimulationNReplicas, 14
 4211         solventSimulationProductionLength, 10.0  [ Units: nanosecond ]
 4212         solventSimulationRealTimeAnalysisInterval, 250.0  [ Units:
 4213             picosecond ]
 4214         solventSimulationRealTimeAnalysisMinimumTime, 500.0 [ Units:
 4215             picosecond
 4216         solventSimulationSamplerMethod, repex  [ Possible values: repex,
 4217             sams, or independent ]
 4218         solventSimulationSamsFlatnessCriteria, logZ-flatness  [ Possible
 4219             values: logZ-flatness, minimum-visits or histogram-flatness ]
 4220         solventSimulationsamsGamma0, 1.0
 4221         solventSimulationTimePerIteration,2.5  [ Units: picosecond ]
 4222 
 4223         Thermo settings:
 4224 
 4225         thermoPh, None  [ Possible values: > 0 ]
 4226         thermoPressure, 1.0  [ Units: bar ]
 4227         thermoRedoxPotential, None  [ Possible values: A valid float.
 4228             Units: millivolts (mV) ]
 4229         thermoTemperature, 298.15  [ Units: kelvin ]
 4230 
 4231         Vacuum engine settings:
 4232 
 4233         vacuumEngineComputePlatform, CPU  [ Possible values: CPU, CUDA,
 4234             OpenCL, or Reference ]
 4235         vacummEngineGpuDeviceIndex, None [ Possible values: 0, 0 1, etc. ]
 4236 
 4237         Vacuum equil output settings:
 4238 
 4239         vacuumEquilOutputCheckpointInterval, 1.0  [ Units: nanosecond ]
 4240         vacuumEquilOutputCheckpointStorageFilename, checkpoint.chk
 4241         vacuumEquilOutputNPTStructure, equil_structure.pdb
 4242         vacuumEquilOutputNVTStructure,None
 4243         vacuumEquilOutputForcefieldCache, db.json
 4244         vacuumEquilOutputLogOutput, equil_simulation.log
 4245         vacuumEquilOutputMinimizedStructure, minimized.pdb
 4246         vacuumEquilOutputIndices, not water   [ Possible value: Any valid
 4247             selection. ]
 4248         vacuumEquilOutputPreminimizedStructure, system.pdb
 4249         vacuumEquilOutputProductionTrajectoryFilename, production_equil.xtc
 4250         vacuumEquilOutputTrajectoryWriteInterval, 20.0  [ Units: picosecond ]
 4251 
 4252         Vacuum equil simulation settings:
 4253 
 4254         vacuumEquilSimulationEquilLength, 0.2  [ Units: nanosecond ]
 4255         vacuumEquilSimulationEquilLengthNVT, None  [ Units: nanosecond ]
 4256         vacuumEquilSimulationMinimizationSteps, 5000
 4257         vacuumEquilSimulationProductionLength, 0.5 [ Units: nanosecond ]
 4258 
 4259         Vacuum forcefield settings:
 4260 
 4261         vacuumForcefieldConstraints, HBonds  [ Possible values: HBonds,
 4262             AllBonds, or HAngles ]
 4263         vacuumForcefields, amber/ff14SB.xml, amber/tip3p_standard.xml
 4264             amber/tip3p_HFE_multivalent.xml amber/phosaa10.xml
 4265             [ Possible values: A space delimited list of valid names. ]
 4266         vacuumForcefieldHydrogenMass, 3.0  [ Units: amu ]
 4267         vacuumForcefieldNonbondedCutoff, 0.9  [ Units: nanometer ]
 4268         vacuumForcefieldNonbondedMethod, nocutoff  [ Possible values: PME
 4269             or NoCutoff ]
 4270         vacuumForcefieldRigidWater, yes,  [ Possible values: yes or no ]
 4271         vacuumForcefieldSmallMoleculeForcefield, openff-2.1.1  [ Possible
 4272             value: A valid forcefield name. ]
 4273 
 4274         Vacuum output settings:
 4275 
 4276         vacuumOutputCheckpointInterval, 1.0  [ Units: nanosecond ]
 4277         vacuumOutputCheckpointStorageFilename, vacuum_checkpoint.nc
 4278         vacuumOutputForcefieldCache, db.json
 4279         vacuumOutputFilename, vacuum.nc
 4280         vacuumOutputIndices, not water   [ Possible value: Any valid
 4281             selection. ]
 4282         vacuumOutputStructure, hybrid_system.pdb
 4283         vacuumOutputPositionsWriteFrequency, 100.0  [ Units: picosecond ]
 4284         vacuumOutputVelocitiesWriteFrequency, None  [ Possible
 4285             values: > 0; Units: picosecond ]
 4286 
 4287         Vacuum simulation settings:
 4288 
 4289         vacuumSimulationEarlyTerminationTargetError, 0.0  [ Units:
 4290             0.0 kilocalorie_per_mole ]
 4291         vacuumSimulationEquilibrationLength, 0.5  [ Units: nanosecond ]
 4292         vacuumSimulationMinimizationSteps, 5000
 4293         vacuumSimulationNReplicas, 14
 4294         vacuumSimulationProductionLength, 2.0  [ Units: nanosecond ]
 4295         vacuumSimulationRealTimeAnalysisInterval, 250.0  [ Units: picosecond ]
 4296         vacuumSimulationRealTimeAnalysisMinimumTime, 500.0  [ Units: picosecond]
 4297         vacuumSimulationSamplerMethod, repex [ Possible values: repex,
 4298             sams, or independent ]
 4299         vacuumSimulationSamsFlatnessCriteria, logZ-flatness  [ Possible
 4300             values: logZ-flatness, minimum-visits or histogram-flatness ]
 4301         vacuumSimulationSamsGamma0, 1.0
 4302         vacuumSimulationTimePerIteration,2.5  [ Units: picosecond ]
 4303 
 4304         Thermo settings:
 4305 
 4306         thermoPh, None  [ Possible values: > 0 ]
 4307         thermoPressure, 0.98692327  [ Units: standard_atmosphere ]
 4308         thermoRedoxPotential, None  [ Possible values: A valid float.
 4309             Units: millivolts (mV) ]
 4310         thermoTemperature, 298.15  [ Units: kelvin ]
 4311 
 4312     A brief description of parameters, taken from OpenFE documentation, is
 4313     provided below:
 4314 
 4315         protocolRepeats: Number of completely independent repeats of the
 4316         entire sampling process.
 4317 
 4318         Integrator settings:
 4319 
 4320         Parameters controlling the LangevinSplittingDynamicsMove integrator
 4321         used for simulation.
 4322 
 4323         integratorBarostatFrequency: Frequency at which volume scaling
 4324             changes should be attempted.
 4325         integratorConstraintTolerance: Tolerance for constraint solver.
 4326         integratorLangevinCollisionRate: Collision frequency.
 4327         integratorNRestartAttempts: Number of attempts to restart from
 4328             Context in case there are NaNs in the energies after
 4329             integration.
 4330         integratorReassignVelocities: Reassign velocities  from the
 4331             Maxwell-Boltzmann distribution at the beginning of each
 4332             Monte Carlo move.
 4333         integratorRemoveCom: Remove the center of mass motion.
 4334         integratorTimestep: Size of the simulation timestep.
 4335 
 4336         Lambda settings:
 4337 
 4338         Lambda protocol parameters, including number of lambda windows and
 4339 
 4340         lambdaElec: List of lambda values for electrostatics. The values of
 4341             0 and 1 imply state A and state B respectively.
 4342         lambdaRestraints: List of lambda values for restraints. The values
 4343             of 0 and 1 imply state A and state B respectively.
 4344         lambdaVdw: List of lamda values for van der Waals. The values of
 4345             of 0 and 1 imply state A and state B respectively.
 4346 
 4347         Partial charge settings:
 4348 
 4349         Parameters for automatically assigning missing partial charges to
 4350         small molecules, including the partial charge method.
 4351 
 4352         partialChargeNaglModel: Model to use for partial charge assignment.
 4353             A value of None implies the use of the latest available
 4354             production AM1BCC model.
 4355         partialChargeNumberOfConformers: Number of conformers to generate
 4356             as part of the partial charge assignment. A value of None
 4357             implies the use of the existing conformer.
 4358         partialChargeOffToolkitBackend: OpenFF toolkit registry backend to
 4359             use for calculating partial charges.
 4360         partialChargeMethod: Method to use for calculating partial charges.
 4361 
 4362         Solvation settings:
 4363 
 4364         Solvation parameters for the system, including the solvent model and
 4365         the solvent padding.
 4366 
 4367         solvationBoxShape: Shape of the periodic solvent box to create.
 4368         solvationBoxSize: Lengths of the unit cell for a solvent box.
 4369         solvationSolventModel: Forcefield water model to use during
 4370             solvation and defining the model properties.
 4371         solvationSolventPadding: Minimum distance from any solute bounding
 4372             sphere to the edge of the box.
 4373 
 4374         Solvent engine settings:
 4375 
 4376         Parameters configuring the compute platform used by the OpenMM to
 4377         perform the simulation.
 4378 
 4379         solventEngineComputePlatform: Platform to use for running OpenMM MD
 4380             calculations.
 4381         solventEngineGpuDeviceIndex: Space delimited list of device indices
 4382             to use for running OpenMM MD calculations.
 4383 
 4384         Solvent equil output settings:
 4385 
 4386         Parameters controlling simulation output during equilibration
 4387         phase of solvent transformation.
 4388 
 4389         solventEquilOutputCheckpointInterval: Frequency to write the
 4390             checkpoint file.
 4391         solventEquilOutputCheckpointStorageFilename: Checkpoint filename.
 4392         solventEquilOutputNPTStructure: NPT structure filename.
 4393         solventEquilOutputNVTStructure: NVT strucure filename.
 4394         solventEquilOutputForcefieldCache: Filename for caching small
 4395             molecule residue templates.
 4396         solventEquilOutputLogOutput: Simulation log filename.
 4397         solventEquilOutputMinimizedStructure: Minimized structire filename.
 4398         solventEquilOutputIndices: Selection string for selecting
 4399             coordinates to write.
 4400         solventEquilOutputPreminimizedStructure: Initial structure filename.
 4401         solventEquilOutputProductionTrajectoryFilename: Trajectory filename.
 4402         solventEquilOutputTrajectoryWriteInterval: Frequency for writing
 4403             velocities to trajectory file.
 4404 
 4405         Solvent equil simulation settings:
 4406 
 4407         Parameters controlling simulation during equilibration phase of
 4408         solvent transformation.
 4409 
 4410         solventEquilSimulationEquilLength: Length of the NPT equilibration
 4411             phase.
 4412         solventEquilSimulationEquiLengthNVT: Length of the NVT equilibration
 4413             phase.
 4414         solventEquilSimulationMinimizationSteps: Maximum number of
 4415             minimization steps to perform.
 4416         solventEquilSimulationProductionLength: Length of the NPT production
 4417             phase.
 4418 
 4419         Solvent forcefield settings:
 4420 
 4421         Parameters to set up the force field with OpenMM Force Fields
 4422         equilibration phase of solvent transformation.
 4423 
 4424         solventForcefieldConstraints:  Constraints to use.
 4425         solventForcefields:  List of valid forcefield paths for all
 4426             components except small molecules.
 4427         solventForcefieldHydrogenMass: Mass to be repartitioned to
 4428             hydrogens from neighboring heavy atoms.
 4429         solventForcefieldNonbondedCutoff: Cutoff for short range nonbonded
 4430             interactions.
 4431         solventForcefieldNonbondedMethod: Method for treating nonbonded
 4432             interactions.
 4433         solventForcefieldRigidWater: Use a rigid water model.
 4434         solventForcefieldSmallMoleculeForcefield: A valid forcefield name
 4435             to use small molecules.
 4436 
 4437         Solvent output settings:
 4438 
 4439         Parameters controlling simulation output during final phase of
 4440         solvent transformation.
 4441 
 4442         solventOutputCheckpointInterval: Frequency to write the checkpoint
 4443             file.
 4444         solventOutputCheckpointStorageFilename: Checkpoint filename.
 4445         solventOutputForcefieldCache: Filename for caching small molecule
 4446             residue templates.
 4447         solventOutputFilename: Trajectory filename.
 4448         solventOutputIndices: Selection string for selecting coordinates to
 4449             write.
 4450         solventOutputStructure: Hybrid topology structure filename.
 4451         solventOutputPositionsWriteFrequency: Frequency for writing
 4452             positions to trajectory file.
 4453         solventOutputVelocitiesWriteFrequency:  Frequency for writing
 4454             velocities to trajectory file.
 4455 
 4456         Solvent simulation settings:
 4457 
 4458         Parameters controlling simulation during final phase of solvent
 4459         transformation.
 4460 
 4461         solventSimulationEarlyTerminationTargetError: Target error for the
 4462             real time analysis measured in kcal/mol. Once the MBAR error of
 4463             the free energy is at or below this value, the simulation will
 4464             be considered complete. The suggested value of 0.12 has shown to
 4465             be effective in both hydration and binding free energy
 4466             benchmarks.
 4467         solventSimulationEquilibrationLength: Length of the equilibration
 4468             phase. The specified value must be divisible by 'integratorTimestep'.
 4469         solventSimulationMinimizationSteps: Maximum number of minimization
 4470             steps to perform.
 4471         solventSimulationNReplicas: Number of replicas to use.
 4472         solventSimulationProductionLength: Length of the production phase.
 4473             The specified value must be divisible by 'integratorTimestep'.
 4474         solventSimulationRealTimeAnalysisMinimumTime: Time interval for
 4475             performing analysis of the free energies. At each interval, real
 4476             time analysis data will be written to a yaml file named
 4477             <outputFileName>_real_time_analysis.yaml. The current error
 4478             in the estimate will also be assessed and the simulation will
 4479             be terminated when it drops below
 4480             'simulationEarlyTerminationTargetError'.
 4481         solventSimulationSamplerMethod: Minimum simulation time after
 4482             which the real time analysis is performed.
 4483         solventSimulationSamplerMethod: Alchemical sampling method to use:
 4484             REPEX (Hamiltonian REPlica EXchange), SAMS (Self-Adjusted
 4485             Mixture Sampling), or Independent (Independently sampled lambda
 4486             windows).
 4487         solventSimulationSamsFlatnessCriteria:Method for assessing when to
 4488             switch to asymptomatically optimal scheme for SAMS.
 4489         solventSimulationsamsGamma0: Initial weight adaptation rate for
 4490             SAMS.
 4491         solventSimulationTimePerIteration: Simulation time between each
 4492             MCMC move attempt
 4493 
 4494         Vacuum engine settings:
 4495 
 4496         Parameters configuring the compute platform used by the OpenMM to
 4497         perform the simulation.
 4498 
 4499         vacuumEngineComputePlatform: Platform to use for running OpenMM MD
 4500             calculations.
 4501         vacuumEngineGpuDeviceIndex: Space delimited list of device indices
 4502             to use for running OpenMM MD calculations.
 4503 
 4504         The rest of the vacuum settings are similar to the solvent settings already
 4505         described under various sections for solvent. The prefix 'vacuum' is used
 4506         for the names of the pramaters instead of the prefix 'solvent.'
 4507 
 4508         Thermo settings:
 4509 
 4510         Thermodynamic parameters, including the temperature and the pressure
 4511         of the system.
 4512 
 4513         thermoPh: Simulation pH
 4514         thermoPressure: Simulation pressure.
 4515         thermoRedoxPotential:Simulation redox potential.
 4516         thermoTemperature: Simulation temperature.
 4517 
 4518     Arguments:
 4519         ParamsOptionName (str): Command line OpenFE RFE parameters option name.
 4520         ParamsOptionValue (str): Comma delimited list of parameter name and value pairs.
 4521         ParamsDefaultInfo (dict): Default values to override selected parameters.
 4522 
 4523     Returns:
 4524         dictionary: Processed parameter name and value pairs.
 4525 
 4526     """
 4527     ParamsInfo = _SetupAbsoluteHydrationFreeEnergyDefaultParametersInfo(ParamsOptionName, ParamsOptionValue)
 4528 
 4529     (ValidParamNames, CanonicalParamNamesMap, ParamsOptionValue, ParamsOptionValueWords) = (
 4530         _ValidateAndCanonicalizeParameterNames(ParamsOptionName, ParamsOptionValue, ParamsInfo, ParamsDefaultInfo)
 4531     )
 4532 
 4533     if re.match("^auto$", ParamsOptionValue, re.I):
 4534         _ProcessOptionOpenFEAbsoluteHydrationFreeEnergyParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 4535         return ParamsInfo
 4536 
 4537     for Index in range(0, len(ParamsOptionValueWords), 2):
 4538         Name = ParamsOptionValueWords[Index].strip()
 4539         Value = ParamsOptionValueWords[Index + 1].strip()
 4540 
 4541         ParamName = CanonicalParamNamesMap[Name.lower()]
 4542         ParamValue = Value
 4543 
 4544         if re.match(
 4545             "^(ProtocolRepeats|IntegratorNRestartAttempts|PartialChargeNumberOfConformers|SolventEquilSimulationMinimizationSteps|SolventSimulationMinimizationSteps|SolventSimulationNReplicas|VacuumEquilSimulationMinimizationSteps|VacuumSimulationMinimizationSteps|VacuumSimulationNReplicas)$",
 4546             ParamName,
 4547             re.I,
 4548         ):
 4549             #  Int > 0
 4550             if not MiscUtil.IsInteger(Value):
 4551                 MiscUtil.PrintError(
 4552                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 4553                     % (Value, ParamName, ParamsOptionName)
 4554                 )
 4555             Value = int(Value)
 4556             if Value <= 0:
 4557                 MiscUtil.PrintError(
 4558                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4559                     % (ParamValue, ParamName, ParamsOptionName)
 4560                 )
 4561             ParamValue = Value
 4562         elif re.match(
 4563             "^(IntegratorConstraintTolerance|SolventForcefieldHydrogenMass|SolventSimulationsamsGamma0|VacuumForcefieldHydrogenMass)$",
 4564             ParamName,
 4565             re.I,
 4566         ):
 4567             # float > 0
 4568             if not MiscUtil.IsFloat(Value):
 4569                 MiscUtil.PrintError(
 4570                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4571                     % (Value, ParamName, ParamsOptionName)
 4572                 )
 4573             Value = float(Value)
 4574             if Value <= 0:
 4575                 MiscUtil.PrintError(
 4576                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4577                     % (ParamValue, ParamName, ParamsOptionName)
 4578                 )
 4579             ParamValue = Value
 4580         elif re.match("^ThermoPh$", ParamName, re.I):
 4581             #  float > 0 or None
 4582             if re.match("^None$", Value, re.I):
 4583                 ParamValue = None
 4584             else:
 4585                 if not MiscUtil.IsFloat(Value):
 4586                     MiscUtil.PrintError(
 4587                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4588                         % (Value, ParamName, ParamsOptionName)
 4589                     )
 4590                 Value = float(Value)
 4591                 if Value <= 0:
 4592                     MiscUtil.PrintError(
 4593                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4594                         % (ParamValue, ParamName, ParamsOptionName)
 4595                     )
 4596                 ParamValue = Value
 4597         elif re.match("^ThermoRedoxPotential$", ParamName, re.I):
 4598             if re.match("^None$", Value, re.I):
 4599                 ParamValue = None
 4600             else:
 4601                 if not MiscUtil.IsFloat(Value):
 4602                     MiscUtil.PrintError(
 4603                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4604                         % (Value, ParamName, ParamsOptionName)
 4605                     )
 4606                 Value = float(Value)
 4607                 ParamValue = Value * openff.units.unit.millivolts
 4608         elif re.match("^(SolventForcefieldNonbondedCutoff|VacuumForcefieldNonbondedCutoff)$", ParamName, re.I):
 4609             #  float > 0 and units nanometer
 4610             if not MiscUtil.IsFloat(Value):
 4611                 MiscUtil.PrintError(
 4612                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4613                     % (Value, ParamName, ParamsOptionName)
 4614                 )
 4615             Value = float(Value)
 4616             if Value <= 0:
 4617                 MiscUtil.PrintError(
 4618                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4619                     % (ParamValue, ParamName, ParamsOptionName)
 4620                 )
 4621             ParamValue = Value * openff.units.unit.nanometer
 4622         elif re.match("^SolvationSolventPadding$", ParamName, re.I):
 4623             #  float > 0 and units nanometer or none
 4624             if re.match("^None$", Value, re.I):
 4625                 ParamValue = None
 4626             else:
 4627                 if not MiscUtil.IsFloat(Value):
 4628                     MiscUtil.PrintError(
 4629                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4630                         % (Value, ParamName, ParamsOptionName)
 4631                     )
 4632                 Value = float(Value)
 4633                 if Value <= 0:
 4634                     MiscUtil.PrintError(
 4635                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4636                         % (ParamValue, ParamName, ParamsOptionName)
 4637                     )
 4638                 ParamValue = Value * openff.units.unit.nanometer
 4639         elif re.match(
 4640             "^(IntegratorReassignVelocities|IntegratorRemoveCom|SolventForcefieldRigidWater|VacuumForcefieldRigidWater)$",
 4641             ParamName,
 4642             re.I,
 4643         ):
 4644             #  bool
 4645             if not re.match("^(yes|no|true|false)$", Value, re.I):
 4646                 MiscUtil.PrintError(
 4647                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: yes or no'
 4648                     % (Value, Name, ParamsOptionName)
 4649                 )
 4650             ParamValue = True if re.match("^(yes|true)$", Value, re.I) else False
 4651         elif re.match("^(LambdaElec|LambdaRestraints|LambdaVdw)$", ParamName, re.I):
 4652             # List of float values between 0 and 1...
 4653             Values = Value.split()
 4654             if len(Values) == 0:
 4655                 MiscUtil.PrintError(
 4656                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain a set of space delimited values\n'
 4657                     % (Value, ParamName, ParamsOptionName)
 4658                 )
 4659             for Value in Values:
 4660                 if not MiscUtil.IsFloat(Value):
 4661                     MiscUtil.PrintError(
 4662                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4663                         % (Value, ParamName, ParamsOptionName)
 4664                     )
 4665                 Value = float(Value)
 4666                 if Value < 0.0 or Value > 1.0:
 4667                     MiscUtil.PrintError(
 4668                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not valid value. Supported values: 0.0 to 1.0\n'
 4669                         % (Value, ParamName, ParamsOptionName)
 4670                     )
 4671             Values = [float(Value) for Value in Values]
 4672             ParamValue = Values
 4673         elif re.match("^IntegratorBarostatFrequency$", ParamName, re.I):
 4674             if not MiscUtil.IsFloat(Value):
 4675                 MiscUtil.PrintError(
 4676                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4677                     % (Value, ParamName, ParamsOptionName)
 4678                 )
 4679             Value = float(Value)
 4680             if Value <= 0:
 4681                 MiscUtil.PrintError(
 4682                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4683                     % (ParamValue, ParamName, ParamsOptionName)
 4684                 )
 4685             ParamValue = Value * openff.units.unit.timestep
 4686         elif re.match("^IntegratorLangevinCollisionRate$", ParamName, re.I):
 4687             if not MiscUtil.IsFloat(Value):
 4688                 MiscUtil.PrintError(
 4689                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4690                     % (Value, ParamName, ParamsOptionName)
 4691                 )
 4692             Value = float(Value)
 4693             if Value <= 0:
 4694                 MiscUtil.PrintError(
 4695                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4696                     % (ParamValue, ParamName, ParamsOptionName)
 4697                 )
 4698             ParamValue = Value / openff.units.unit.picosecond
 4699         elif re.match("^IntegratorTimestep$", ParamName, re.I):
 4700             # float > 0 femtosecond
 4701             if not MiscUtil.IsFloat(Value):
 4702                 MiscUtil.PrintError(
 4703                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4704                     % (Value, ParamName, ParamsOptionName)
 4705                 )
 4706             Value = float(Value)
 4707             if Value <= 0:
 4708                 MiscUtil.PrintError(
 4709                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4710                     % (ParamValue, ParamName, ParamsOptionName)
 4711                 )
 4712             ParamValue = Value * openff.units.unit.femtosecond
 4713         elif re.match(
 4714             "^(SolventEquilOutputTrajectoryWriteInterval|SolventOutputPositionsWriteFrequency|SolventSimulationRealTimeAnalysisInterval|SolventSimulationRealTimeAnalysisMinimumTime|SolventSimulationTimePerIteration|VacuumEquilOutputTrajectoryWriteInterval|VacuumOutputPositionsWriteFrequency|VacuumSimulationRealTimeAnalysisInterval|VacuumSimulationRealTimeAnalysisMinimumTime|VacuumSimulationTimePerIteration)$",
 4715             ParamName,
 4716             re.I,
 4717         ):
 4718             #  float > 0 picosecond
 4719             if not MiscUtil.IsFloat(Value):
 4720                 MiscUtil.PrintError(
 4721                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4722                     % (Value, ParamName, ParamsOptionName)
 4723                 )
 4724             Value = float(Value)
 4725             if Value <= 0:
 4726                 MiscUtil.PrintError(
 4727                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4728                     % (ParamValue, ParamName, ParamsOptionName)
 4729                 )
 4730             ParamValue = Value * openff.units.unit.picosecond
 4731         elif re.match(
 4732             "^(SolventEquilOutputCheckpointInterval|SolventEquilSimulationEquilLength|SolventEquilSimulationEquiLengthNVT|SolventEquilSimulationProductionLength|SolventOutputCheckpointInterval|SolventSimulationEquilibrationLength|SolventSimulationProductionLength|VacuumEquilOutputCheckpointInterval|VacuumEquilSimulationEquilLength|VacuumEquilSimulationProductionLength|VacuumOutputCheckpointInterval|VacuumSimulationEquilibrationLength|VacuumSimulationProductionLength)$",
 4733             ParamName,
 4734             re.I,
 4735         ):
 4736             #  float > 0 nanosecond
 4737             if not MiscUtil.IsFloat(Value):
 4738                 MiscUtil.PrintError(
 4739                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4740                     % (Value, ParamName, ParamsOptionName)
 4741                 )
 4742             Value = float(Value)
 4743             if Value <= 0:
 4744                 MiscUtil.PrintError(
 4745                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4746                     % (ParamValue, ParamName, ParamsOptionName)
 4747                 )
 4748             ParamValue = Value * openff.units.unit.nanosecond
 4749         elif re.match(
 4750             "^(SolventOutputVelocitiesWriteFrequency|VacuumOutputVelocitiesWriteFrequency)$", ParamName, re.I
 4751         ):
 4752             #  float > 0 picosecond or none
 4753             if re.match("^None$", Value, re.I):
 4754                 ParamValue = None
 4755             else:
 4756                 if not MiscUtil.IsFloat(Value):
 4757                     MiscUtil.PrintError(
 4758                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4759                         % (Value, ParamName, ParamsOptionName)
 4760                     )
 4761                 Value = float(Value)
 4762                 if Value <= 0:
 4763                     MiscUtil.PrintError(
 4764                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4765                         % (ParamValue, ParamName, ParamsOptionName)
 4766                     )
 4767                 ParamValue = Value * openff.units.unit.picosecond
 4768         elif re.match("^(VacuumEquilSimulationEquilLengthNVT)$", ParamName, re.I):
 4769             #  float > 0 nanosecond or none
 4770             if re.match("^None$", Value, re.I):
 4771                 ParamValue = None
 4772             else:
 4773                 if not MiscUtil.IsFloat(Value):
 4774                     MiscUtil.PrintError(
 4775                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4776                         % (Value, ParamName, ParamsOptionName)
 4777                     )
 4778                 Value = float(Value)
 4779                 if Value <= 0:
 4780                     MiscUtil.PrintError(
 4781                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4782                         % (ParamValue, ParamName, ParamsOptionName)
 4783                     )
 4784                 ParamValue = Value * openff.units.unit.nanosecond
 4785         elif re.match("^PartialChargeMethod$", ParamName, re.I):
 4786             if not re.match("^(AM1BCC|AM1BCCELF10|Espaloma|NAGL)$", Value, re.I):
 4787                 MiscUtil.PrintError(
 4788                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: AM1BCC, AM1BCCELF10, Espaloma, or NAGL'
 4789                     % (Value, Name, ParamsOptionName)
 4790                 )
 4791             ParamValue = Value.lower()
 4792         elif re.match("^PartialChargeOffToolkitBackend$", ParamName, re.I):
 4793             if not re.match("^(AmberTools|OpenEye|RDKit)$", Value, re.I):
 4794                 MiscUtil.PrintError(
 4795                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: AmberTools, OpenEye, or RDKit'
 4796                     % (Value, Name, ParamsOptionName)
 4797                 )
 4798             ParamValue = Value.lower()
 4799         elif re.match("^SolvationBoxShape$", ParamName, re.I):
 4800             if not re.match("^(cube|dodecahedron|octahedron)$", Value, re.I):
 4801                 MiscUtil.PrintError(
 4802                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: cube, dodecahedron, or octahedron'
 4803                     % (Value, Name, ParamsOptionName)
 4804                 )
 4805             ParamValue = Value.lower()
 4806         elif re.match("^SolvationBoxSize$", ParamName, re.I):
 4807             # List of X, Y, Z values...
 4808             if re.match("^None$", Value, re.I):
 4809                 ParamValue = None
 4810             else:
 4811                 Values = Value.split()
 4812                 if len(Values) != 3:
 4813                     MiscUtil.PrintError(
 4814                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain a set of three space delimited values.\n'
 4815                         % (Value, ParamName, ParamsOptionName)
 4816                     )
 4817                 for Value in Values:
 4818                     if not MiscUtil.IsFloat(Value):
 4819                         MiscUtil.PrintError(
 4820                             'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4821                             % (Value, ParamName, ParamsOptionName)
 4822                         )
 4823                 Values = [float(Value) for Value in Values]
 4824                 ParamValue = Values * openff.units.unit.nanometer
 4825         elif re.match("^SolvationSolventModel$", ParamName, re.I):
 4826             if not re.match("^(tip3p|spce|tip4pew|tip5p)$", Value, re.I):
 4827                 MiscUtil.PrintError(
 4828                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: tip3p, spce, tip4pew, or tip5p'
 4829                     % (Value, Name, ParamsOptionName)
 4830                 )
 4831             ParamValue = Value.lower()
 4832         elif re.match("^(SolventEngineComputePlatform|VacuumEngineComputePlatform)$", ParamName, re.I):
 4833             if not re.match("^(CPU|CUDA|OpenCL|Reference)$", Value, re.I):
 4834                 MiscUtil.PrintError(
 4835                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: CPU, CUDA, OpenCL, or Reference'
 4836                     % (Value, Name, ParamsOptionName)
 4837                 )
 4838             ParamValue = Value
 4839         elif re.match("^(SolventEngineGpuDeviceIndex|VacuumEngineGpuDeviceIndex)$", ParamName, re.I):
 4840             #  Comma delimited string values...
 4841             DeviceIndices = Value.split()
 4842             if len(DeviceIndices) == 0:
 4843                 MiscUtil.PrintError(
 4844                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain space delimited list of device indices.\n'
 4845                     % (Value, ParamName, ParamsOptionName)
 4846                 )
 4847             for DeviceIndex in DeviceIndices:
 4848                 if not MiscUtil.IsInteger(DeviceIndex):
 4849                     MiscUtil.PrintError(
 4850                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 4851                         % (DeviceIndex, ParamName, ParamsOptionName)
 4852                     )
 4853                 DeviceIndices = [int(DeviceIndex) for DeviceIndex in DeviceIndices]
 4854             ParamValue = DeviceIndices
 4855         elif re.match("^(SolventForcefieldConstraints|VacuumForcefieldConstraints)$", ParamName, re.I):
 4856             if not re.match("^(HBonds|AllBonds|HAngles|None)$", Value, re.I):
 4857                 MiscUtil.PrintError(
 4858                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: HBonds, AllBonds, HAngles, or None'
 4859                     % (Value, Name, ParamsOptionName)
 4860                 )
 4861             ParamValue = None if re.match("^None$", Value, re.I) else Value.lower()
 4862         elif re.match("^(SolventForcefields|VacuumForcefields)$", ParamName, re.I):
 4863             #  List of string values.....
 4864             Values = Value.split()
 4865             if len(Values) == 0:
 4866                 MiscUtil.PrintError(
 4867                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain a space delimited list of values..\n'
 4868                     % (Value, ParamName, ParamsOptionName)
 4869                 )
 4870             ParamValue = Values
 4871         elif re.match("^(SolventForcefieldNonbondedMethod|VacuumForcefieldNonbondedMethod)$", ParamName, re.I):
 4872             if not re.match("^(PME|NoCutoff)$", Value, re.I):
 4873                 MiscUtil.PrintError(
 4874                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is may be a valid OpenFE value. Supported values: PME or NoCutoff'
 4875                     % (Value, Name, ParamsOptionName)
 4876                 )
 4877             ParamValue = Value.lower()
 4878         elif re.match(
 4879             "^(SolventSimulationEarlyTerminationTargetError|VacuumSimulationEarlyTerminationTargetError)$",
 4880             ParamName,
 4881             re.I,
 4882         ):
 4883             # float >= 0 units kilocalorie_per_mole
 4884             if not MiscUtil.IsFloat(Value):
 4885                 MiscUtil.PrintError(
 4886                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4887                     % (Value, ParamName, ParamsOptionName)
 4888                 )
 4889             Value = float(Value)
 4890             if Value < 0:
 4891                 MiscUtil.PrintError(
 4892                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4893                     % (ParamValue, ParamName, ParamsOptionName)
 4894                 )
 4895             ParamValue = Value * openff.units.unit.kilocalorie_per_mole
 4896         elif re.match("^(SolventSimulationSamplerMethod|VacuumSimulationSamplerMethod)$", ParamName, re.I):
 4897             if not re.match("^(repex|sams|independent)$", Value, re.I):
 4898                 MiscUtil.PrintError(
 4899                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: repex, sams, or independent'
 4900                     % (Value, Name, ParamsOptionName)
 4901                 )
 4902             ParamValue = Value.lower()
 4903         elif re.match(
 4904             "^(SolventSimulationSamsFlatnessCriteria|VacuumSimulationSamsFlatnessCriteria)$", ParamName, re.I
 4905         ):
 4906             if not re.match("^(logz-flatness|minimum-visits|histogram-flatness)$", Value, re.I):
 4907                 MiscUtil.PrintError(
 4908                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: logz-flatness, minimum-visits, or histogram-flatness'
 4909                     % (Value, Name, ParamsOptionName)
 4910                 )
 4911             ParamValue = Value.lower()
 4912         elif re.match("^ThermoPressure$", ParamName, re.I):
 4913             #  float > 0 and units bar
 4914             if not MiscUtil.IsFloat(Value):
 4915                 MiscUtil.PrintError(
 4916                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4917                     % (Value, ParamName, ParamsOptionName)
 4918                 )
 4919             Value = float(Value)
 4920             if Value <= 0:
 4921                 MiscUtil.PrintError(
 4922                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 4923                     % (ParamValue, ParamName, ParamsOptionName)
 4924                 )
 4925             ParamValue = Value * openff.units.unit.bar
 4926         elif re.match("^ThermoTemperature$", ParamName, re.I):
 4927             # float >= 0 and units kelvin
 4928             if not MiscUtil.IsFloat(Value):
 4929                 MiscUtil.PrintError(
 4930                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 4931                     % (Value, ParamName, ParamsOptionName)
 4932                 )
 4933             Value = float(Value)
 4934             if Value < 0:
 4935                 MiscUtil.PrintError(
 4936                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: >= 0\n'
 4937                     % (ParamValue, ParamName, ParamsOptionName)
 4938                 )
 4939             ParamValue = Value * openff.units.unit.kelvin
 4940         else:
 4941             # Str or None...
 4942             ParamValue = None if re.match("^None$", Value, re.I) else Value
 4943 
 4944         # Set value...
 4945         ParamsInfo[ParamName] = ParamValue
 4946 
 4947     # Handle parameters with possible auto values...
 4948     _ProcessOptionOpenFEAbsoluteHydrationFreeEnergyParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 4949 
 4950     return ParamsInfo
 4951 
 4952 
 4953 def _ProcessOptionOpenFEAbsoluteHydrationFreeEnergyParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 4954     """Process parameters with possible auto values and perform validation."""
 4955 
 4956     # Validate solvation parameter values...
 4957     ParamName1 = "SolvationBoxSize"
 4958     ParamValue1 = ParamsInfo[ParamName1]
 4959     ParamName2 = "SolvationSolventPadding"
 4960     ParamValue2 = ParamsInfo[ParamName2]
 4961     if ParamsInfo[ParamName1] is not None and ParamsInfo[ParamName2] is not None:
 4962         MiscUtil.PrintError(
 4963             'The parameter values, %s and %s, specified for parameter names, %s and %s, using "%s" option is not a valid value. You must specify only one of these values.\n'
 4964             % (ParamValue1, ParamValue2, ParamName1, ParamName2, ParamsOptionName)
 4965         )
 4966 
 4967     ParamName1 = "LambdaElec"
 4968     ParamValue1Count = len(ParamsInfo[ParamName1])
 4969     ParamName2 = "LambdaRestraints"
 4970     ParamValue2Count = len(ParamsInfo[ParamName2])
 4971     ParamName3 = "LambdaVdw"
 4972     ParamValue3Count = len(ParamsInfo[ParamName3])
 4973     if ParamValue1Count != ParamValue2Count or ParamValue1Count != ParamValue3Count:
 4974         MiscUtil.PrintError(
 4975             'The number of values - %s, %s, and %s - specified for parameter names - %s, %s, and %s, using "%s" option are not valid. You must specify same number of values for these parameters.'
 4976             % (
 4977                 ParamValue1Count,
 4978                 ParamValue2Count,
 4979                 ParamValue3Count,
 4980                 ParamName1,
 4981                 ParamName2,
 4982                 ParamName3,
 4983                 ParamsOptionName,
 4984             )
 4985         )
 4986 
 4987     _ProcessPartialChargeMethodAbsoluteHydrationFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 4988     _ProcessPartialChargeNaglAbsoluteHydrationFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 4989 
 4990 
 4991 def _ProcessPartialChargeMethodAbsoluteHydrationFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 4992     """Process  PartialChargeMethod AHFE paramater."""
 4993 
 4994     _ProcessPartialChargeMethodFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 4995 
 4996 
 4997 def _ProcessPartialChargeNaglAbsoluteHydrationFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 4998     """Process  PartialChargeNaglModel AHFE paramater."""
 4999 
 5000     _ProcessPartialChargeNaglFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 5001 
 5002 
 5003 def _SetupAbsoluteHydrationFreeEnergyDefaultParametersInfo(ParamsOptionName, ParamsOptionValue):
 5004     """Setup AHFE default parameters information using the current AHFE settings."""
 5005 
 5006     ParamsInfo = {}
 5007 
 5008     AHFESettings = AbsoluteSolvationProtocol.default_settings()
 5009     AHFEParametersMap = _SetupMapForAbsoluteHydrationFreeEnergyParameters()
 5010 
 5011     for ParamName in AHFEParametersMap.keys():
 5012         AHFEParamGroupName, AHFEParamName = AHFEParametersMap[ParamName]
 5013         if AHFEParamGroupName is None:
 5014             if hasattr(AHFESettings, AHFEParamName):
 5015                 ParamsInfo[ParamName] = getattr(AHFESettings, AHFEParamName)
 5016             else:
 5017                 MiscUtil.PrintInfo(
 5018                     'The OpenFE AHFE settings name, %s, corresponding to AHFE parameter name, %s, specified using option "%s" is not available. Ignoring parameter...'
 5019                     % (AHFEParamName, ParamName, ParamsOptionName)
 5020                 )
 5021         else:
 5022             AHFEParamGroupSettings = (
 5023                 getattr(AHFESettings, AHFEParamGroupName) if hasattr(AHFESettings, AHFEParamGroupName) else None
 5024             )
 5025             if AHFEParamGroupSettings is not None and hasattr(AHFEParamGroupSettings, AHFEParamName):
 5026                 ParamsInfo[ParamName] = getattr(AHFEParamGroupSettings, AHFEParamName)
 5027             else:
 5028                 MiscUtil.PrintInfo(
 5029                     'The OpenFE AHFE parameter name, %s, for settings, %s, corresponding to AHFE parameter name, %s, specified using option "%s" is not available. Ignoring parameter...'
 5030                     % (AHFEParamName, AHFEParamGroupName, ParamName, ParamsOptionName)
 5031                 )
 5032 
 5033     return ParamsInfo
 5034 
 5035 
 5036 def _SetupMapForAbsoluteHydrationFreeEnergyParameters():
 5037     """Map relative free energy option paramater names to OpenFE absolute
 5038     hydration free energy settings.
 5039     """
 5040 
 5041     AHFEParametersMap = {
 5042         "ProtocolRepeats": [None, "protocol_repeats"],
 5043         "IntegratorBarostatFrequency": ["integrator_settings", "barostat_frequency"],
 5044         "IntegratorConstraintTolerance": ["integrator_settings", "constraint_tolerance"],
 5045         "IntegratorLangevinCollisionRate": ["integrator_settings", "langevin_collision_rate"],
 5046         "IntegratorNRestartAttempts": ["integrator_settings", "n_restart_attempts"],
 5047         "IntegratorReassignVelocities": ["integrator_settings", "reassign_velocities"],
 5048         "IntegratorRemoveCom": ["integrator_settings", "remove_com"],
 5049         "IntegratorTimestep": ["integrator_settings", "timestep"],
 5050         "LambdaElec": ["lambda_settings", "lambda_elec"],
 5051         "LambdaRestraints": ["lambda_settings", "lambda_restraints"],
 5052         "LambdaVdw": ["lambda_settings", "lambda_vdw"],
 5053         "PartialChargeNaglModel": ["partial_charge_settings", "nagl_model"],
 5054         "PartialChargeNumberOfConformers": ["partial_charge_settings", "number_of_conformers"],
 5055         "PartialChargeOffToolkitBackend": ["partial_charge_settings", "off_toolkit_backend"],
 5056         "PartialChargeMethod": ["partial_charge_settings", "partial_charge_method"],
 5057         "SolvationBoxShape": ["solvation_settings", "box_shape"],
 5058         "SolvationBoxSize": ["solvation_settings", "box_size"],
 5059         "SolvationSolventModel": ["solvation_settings", "solvent_model"],
 5060         "SolvationSolventPadding": ["solvation_settings", "solvent_padding"],
 5061         "SolventEngineComputePlatform": ["solvent_engine_settings", "compute_platform"],
 5062         "SolventEngineGpuDeviceIndex": ["solvent_engine_settings", "gpu_device_index"],
 5063         "SolventEquilOutputCheckpointInterval": ["solvent_equil_output_settings", "checkpoint_interval"],
 5064         "SolventEquilOutputCheckpointStorageFilename": ["solvent_equil_output_settings", "checkpoint_storage_filename"],
 5065         "SolventEquilOutputNPTStructure": ["solvent_equil_output_settings", "equil_npt_structure"],
 5066         "SolventEquilOutputNVTStructure": ["solvent_equil_output_settings", "equil_nvt_structure"],
 5067         "SolventEquilOutputForcefieldCache": ["solvent_equil_output_settings", "forcefield_cache"],
 5068         "SolventEquilOutputLogOutput": ["solvent_equil_output_settings", "log_output"],
 5069         "SolventEquilOutputMinimizedStructure": ["solvent_equil_output_settings", "minimized_structure"],
 5070         "SolventEquilOutputIndices": ["solvent_equil_output_settings", "output_indices"],
 5071         "SolventEquilOutputPreminimizedStructure": ["solvent_equil_output_settings", "preminimized_structure"],
 5072         "SolventEquilOutputProductionTrajectoryFilename": [
 5073             "solvent_equil_output_settings",
 5074             "production_trajectory_filename",
 5075         ],
 5076         "SolventEquilOutputTrajectoryWriteInterval": ["solvent_equil_output_settings", "trajectory_write_interval"],
 5077         "SolventEquilSimulationEquilLength": ["solvent_equil_simulation_settings", "equilibration_length"],
 5078         "SolventEquilSimulationEquiLengthNVT": ["solvent_equil_simulation_settings", "equilibration_length_nvt"],
 5079         "SolventEquilSimulationMinimizationSteps": ["solvent_equil_simulation_settings", "minimization_steps"],
 5080         "SolventEquilSimulationProductionLength": ["solvent_equil_simulation_settings", "production_length"],
 5081         "SolventForcefieldConstraints": ["solvent_forcefield_settings", "constraints"],
 5082         "SolventForcefields": ["solvent_forcefield_settings", "forcefields"],
 5083         "SolventForcefieldHydrogenMass": ["solvent_forcefield_settings", "hydrogen_mass"],
 5084         "SolventForcefieldNonbondedCutoff": ["solvent_forcefield_settings", "nonbonded_cutoff"],
 5085         "SolventForcefieldNonbondedMethod": ["solvent_forcefield_settings", "nonbonded_method"],
 5086         "SolventForcefieldRigidWater": ["solvent_forcefield_settings", "rigid_water"],
 5087         "SolventForcefieldSmallMoleculeForcefield": ["solvent_forcefield_settings", "small_molecule_forcefield"],
 5088         "SolventOutputCheckpointInterval": ["solvent_output_settings", "checkpoint_interval"],
 5089         "SolventOutputCheckpointStorageFilename": ["solvent_output_settings", "checkpoint_storage_filename"],
 5090         "SolventOutputForcefieldCache": ["solvent_output_settings", "forcefield_cache"],
 5091         "SolventOutputFilename": ["solvent_output_settings", "output_filename"],
 5092         "SolventOutputIndices": ["solvent_output_settings", "output_indices"],
 5093         "SolventOutputStructure": ["solvent_output_settings", "output_structure"],
 5094         "SolventOutputPositionsWriteFrequency": ["solvent_output_settings", "positions_write_frequency"],
 5095         "SolventOutputVelocitiesWriteFrequency": ["solvent_output_settings", "velocities_write_frequency"],
 5096         "SolventSimulationEarlyTerminationTargetError": [
 5097             "solvent_simulation_settings",
 5098             "early_termination_target_error",
 5099         ],
 5100         "SolventSimulationEquilibrationLength": ["solvent_simulation_settings", "equilibration_length"],
 5101         "SolventSimulationMinimizationSteps": ["solvent_simulation_settings", "minimization_steps"],
 5102         "SolventSimulationNReplicas": ["solvent_simulation_settings", "n_replicas"],
 5103         "SolventSimulationProductionLength": ["solvent_simulation_settings", "production_length"],
 5104         "SolventSimulationRealTimeAnalysisInterval": ["solvent_simulation_settings", "real_time_analysis_interval"],
 5105         "SolventSimulationRealTimeAnalysisMinimumTime": [
 5106             "solvent_simulation_settings",
 5107             "real_time_analysis_minimum_time",
 5108         ],
 5109         "SolventSimulationSamplerMethod": ["solvent_simulation_settings", "sampler_method"],
 5110         "SolventSimulationSamsFlatnessCriteria": ["solvent_simulation_settings", "sams_flatness_criteria"],
 5111         "SolventSimulationsamsGamma0": ["solvent_simulation_settings", "sams_gamma0"],
 5112         "SolventSimulationTimePerIteration": ["solvent_simulation_settings", "time_per_iteration"],
 5113         "ThermoPh": ["thermo_settings", "ph"],
 5114         "ThermoPressure": ["thermo_settings", "pressure"],
 5115         "ThermoRedoxPotential": ["thermo_settings", "redox_potential"],
 5116         "ThermoTemperature": ["thermo_settings", "temperature"],
 5117         "VacuumEngineComputePlatform": ["vacuum_engine_settings", "compute_platform"],
 5118         "VacuumEngineGpuDeviceIndex": ["vacuum_engine_settings", "gpu_device_index"],
 5119         "VacuumEquilOutputCheckpointInterval": ["vacuum_equil_output_settings", "checkpoint_interval"],
 5120         "VacuumEquilOutputCheckpointStorageFilename": ["vacuum_equil_output_settings", "checkpoint_storage_filename"],
 5121         "VacuumEquilOutputNPTStructure": ["vacuum_equil_output_settings", "equil_npt_structure"],
 5122         "VacuumEquilOutputNVTStructure": ["vacuum_equil_output_settings", "equil_nvt_structure"],
 5123         "VacuumEquilOutputForcefieldCache": ["vacuum_equil_output_settings", "forcefield_cache"],
 5124         "VacuumEquilOutputLogOutput": ["vacuum_equil_output_settings", "log_output"],
 5125         "VacuumEquilOutputMinimizedStructure": ["vacuum_equil_output_settings", "minimized_structure"],
 5126         "VacuumEquilOutputIndices": ["vacuum_equil_output_settings", "output_indices"],
 5127         "VacuumEquilOutputPreminimizedStructure": ["vacuum_equil_output_settings", "preminimized_structure"],
 5128         "VacuumEquilOutputProductionTrajectoryFilename": [
 5129             "vacuum_equil_output_settings",
 5130             "production_trajectory_filename",
 5131         ],
 5132         "VacuumEquilOutputTrajectoryWriteInterval": ["vacuum_equil_output_settings", "trajectory_write_interval"],
 5133         "VacuumEquilSimulationEquilLength": ["vacuum_equil_simulation_settings", "equilibration_length"],
 5134         "VacuumEquilSimulationEquilLengthNVT": ["vacuum_equil_simulation_settings", "equilibration_length_nvt"],
 5135         "VacuumEquilSimulationMinimizationSteps": ["vacuum_equil_simulation_settings", "minimization_steps"],
 5136         "VacuumEquilSimulationProductionLength": ["vacuum_equil_simulation_settings", "production_length"],
 5137         "VacuumForcefieldConstraints": ["vacuum_forcefield_settings", "constraints"],
 5138         "VacuumForcefields": ["vacuum_forcefield_settings", "forcefields"],
 5139         "VacuumForcefieldHydrogenMass": ["vacuum_forcefield_settings", "hydrogen_mass"],
 5140         "VacuumForcefieldNonbondedCutoff": ["vacuum_forcefield_settings", "nonbonded_cutoff"],
 5141         "VacuumForcefieldNonbondedMethod": ["vacuum_forcefield_settings", "nonbonded_method"],
 5142         "VacuumForcefieldRigidWater": ["vacuum_forcefield_settings", "rigid_water"],
 5143         "VacuumForcefieldSmallMoleculeForcefield": ["vacuum_forcefield_settings", "small_molecule_forcefield"],
 5144         "VacuumOutputCheckpointInterval": ["vacuum_output_settings", "checkpoint_interval"],
 5145         "VacuumOutputCheckpointStorageFilename": ["vacuum_output_settings", "checkpoint_storage_filename"],
 5146         "VacuumOutputForcefieldCache": ["vacuum_output_settings", "forcefield_cache"],
 5147         "VacuumOutputFilename": ["vacuum_output_settings", "output_filename"],
 5148         "VacuumOutputIndices": ["vacuum_output_settings", "output_indices"],
 5149         "VacuumOutputStructure": ["vacuum_output_settings", "output_structure"],
 5150         "VacuumOutputPositionsWriteFrequency": ["vacuum_output_settings", "positions_write_frequency"],
 5151         "VacuumOutputVelocitiesWriteFrequency": ["vacuum_output_settings", "velocities_write_frequency"],
 5152         "VacuumSimulationEarlyTerminationTargetError": ["vacuum_simulation_settings", "early_termination_target_error"],
 5153         "VacuumSimulationEquilibrationLength": ["vacuum_simulation_settings", "equilibration_length"],
 5154         "VacuumSimulationMinimizationSteps": ["vacuum_simulation_settings", "minimization_steps"],
 5155         "VacuumSimulationNReplicas": ["vacuum_simulation_settings", "n_replicas"],
 5156         "VacuumSimulationProductionLength": ["vacuum_simulation_settings", "production_length"],
 5157         "VacuumSimulationRealTimeAnalysisInterval": ["vacuum_simulation_settings", "real_time_analysis_interval"],
 5158         "VacuumSimulationRealTimeAnalysisMinimumTime": [
 5159             "vacuum_simulation_settings",
 5160             "real_time_analysis_minimum_time",
 5161         ],
 5162         "VacuumSimulationSamplerMethod": ["vacuum_simulation_settings", "sampler_method"],
 5163         "VacuumSimulationSamsFlatnessCriteria": ["vacuum_simulation_settings", "sams_flatness_criteria"],
 5164         "VacuumSimulationSamsGamma0": ["vacuum_simulation_settings", "sams_gamma0"],
 5165         "VacuumSimulationTimePerIteration": ["vacuum_simulation_settings", "time_per_iteration"],
 5166     }
 5167 
 5168     return AHFEParametersMap
 5169 
 5170 
 5171 def SetupAbsoluteBindingFreeEnergySettings(ParamsOptionName, ParamsInfo):
 5172     """Setup absolute binding free energy protocol settings to calculate ABFE.
 5173 
 5174     The ParamsInfo is a comma delimited list of parameter name and value pairs
 5175     returned by ProcessOptionOpenFEAbsoluteBindingFreeEnergyParameters().
 5176 
 5177     Arguments:
 5178         ParamsOptionName (str): Command line OpenFE RFE parameters option name.
 5179         ParamsInfo (dict): Parameter name and value pairs.
 5180 
 5181     Returns:
 5182         object: OpenFE AbsoluteBindingProtocol settings object.
 5183 
 5184     """
 5185 
 5186     from openfe.protocols.openmm_afe import AbsoluteBindingProtocol
 5187 
 5188     ABFESettings = AbsoluteBindingProtocol.default_settings()
 5189     ABFEParametersMap = _SetupMapForAbsoluteBindingFreeEnergyParameters()
 5190 
 5191     _UpdateOpenFESettings("ABFE", ParamsOptionName, ParamsInfo, ABFESettings, ABFEParametersMap)
 5192 
 5193     return ABFESettings
 5194 
 5195 
 5196 def ProcessOptionOpenFEAbsoluteBindingFreeEnergyParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo=None):
 5197     """Process parameters for ABFE parameters option and return a map
 5198     containing processed parameter names and values.
 5199 
 5200     The ParamsOptionValue is a comma delimited list of parameter name and value pairs
 5201     to setup ABFE calculations.
 5202 
 5203     The default values are automatically updated to match settings provided by
 5204     OpenFE module AbsoluteBindingProtocol.
 5205 
 5206     You must specify valid OpenFE values for these parameters. An extensive
 5207     validation is not performed.
 5208 
 5209     The supported parameter names along with their default and possible
 5210     values are shown below:
 5211 
 5212         protocolRepeats, 3
 5213 
 5214         Complex equil output settings:
 5215 
 5216         complexEquilOutputCheckpointInterval, 1  [ Units: nanosecond ]
 5217         complexEquilOutputCheckpointStorageFilename, checkpoint.chk
 5218         complexEquilOutputEquilNPTStructure, equil_npt_structure.pdb
 5219         complexEquilOutputEquilNVTstructure, equil_nvt_structure.pdb
 5220         complexEquilOutputForcefieldCache, db.json
 5221         complexEquilOutputLogOutput, production_equil_simulation.log
 5222         complexEquilOutputMinimizedStructure, minimized.pdb
 5223         complexEquilOutputIndices, all  [ Possible value: Any valid
 5224             selection. ]
 5225         complexEquilOutputPremnimizedStructure, system.pdb
 5226         complexEquilOutputProductionTrajectoryFilename, production_equil.xtc
 5227         complexEquilOutputTrajectoryWriteInterval,  20.0  [ Units:
 5228             picosecond ]
 5229 
 5230         Complex equil simulation settings:
 5231 
 5232         complexEquilSimulationEquilibrationLength, 0.5 [ Units: nanosecond ]
 5233         complexEquilSimulationEquilibrationLengthNVT, 0.25   [ Units:
 5234             nanosecond ]
 5235         complexEquilSimulationMinimizationSteps, 5000
 5236         complexEquilSimulationProductionLength, 5.0  [ Units: nanosecond ]
 5237 
 5238         Complex lambda settings:
 5239 
 5240         complexLambdaElec, 0.0 0.0 0.0 0.0 0.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6
 5241             0.7 0.8 0.9 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
 5242             1.0 1.0  [ Possible values: A space delimited list of values
 5243             between 0.0 and 1.0 ]
 5244         complexLambdaRestraints, 0.0 0.2 0.4 0.6 0.8 1.0 1.0 1.0 1.0 1.0
 5245             1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
 5246             1.0 1.0 1.0 1.0  [ Possible values: A space delimited list of
 5247             values between 0.0 and 1.0 ]
 5248         complexLambdaVdw, 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
 5249             0.0 0.0 0.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.65 0.7 0.75 0.8 0.85
 5250             0.9 0.95 1.0  [ Possible values: A space delimited list of values
 5251             between 0.0 and 1.0 ]
 5252 
 5253         Complex output settings:
 5254 
 5255         complexOutputCheckpointInterval, 1.0  [ Units: nanosecond ]
 5256         complexOutputCheckpointStorageFilename, complex_checkpoint.nc
 5257         complexOutputForcefieldCache, db.json
 5258         complexOutputFilename, complex.nc
 5259         complexOutputIndices, not water   [ Possible value: Any valid
 5260             selection. ]
 5261         complexOutputStructure, alchemical_system.pdb
 5262         complexOutputPositionsWriteFrequency, 100  [ Units: picosecond ]
 5263         complexOutputVelocitiesWriteFrequency, None  [ Possible
 5264             values: > 0; Units: picosecond ]
 5265 
 5266         Complex simulation settings:
 5267 
 5268         complexSimulationEarlyTerminationTargetError, 0.0  [ Units:
 5269             kilocalorie_per_mole ]
 5270         complexSimulationEquilibrationLength,  1.0  [ Units: nanosecond ]
 5271         complexSimulationMinimizationSteps, 5000
 5272         complexSimulationNReplicas, 30
 5273         complexSimulationProductionLength, 10.0  [ Units: nanosecond ]
 5274         complexSimulationRealTimeAnalysisInterval, 250.0  [ Units:
 5275             picosecond ]
 5276         complexSimulationRealTimeAnalysisMinimumTime, 500.0  [ Units:
 5277             picosecond ]
 5278         complexSimulationSamplerMethod, repex  [ Possible values: repex,
 5279             sams, or independent ]
 5280         complexSimulationSamsFlatnessCriteria, logZ-flatness  [ Possible
 5281             values: logZ-flatness, minimum-visits or histogram-flatness ]
 5282         complexSimulationSamsGamma0, 1.0
 5283         complexSimulationTimePerIteration, 2.5   [ Units: picosecond ]
 5284 
 5285         Complex solvation settings:
 5286 
 5287         complexSolvationBoxShape, dodecahedron  [  Possible values: cube,
 5288             dodecahedron, or octahedron ]
 5289         complexSolvationBoxSize, None  [ Possible value: A triplet of space
 5290             X Y Z values; Units: nanometer ]
 5291         complexSolvationSolventModel, tip3p  [ Possible values: tip3p, spce,
 5292             tip4pew, or tip5p ]
 5293         complexSolvationSolventPadding, 1.0  [ Units: nanometer ]
 5294 
 5295         Engine settings:
 5296 
 5297         engineComputePlatform, CPU  [ Possible values: CPU, CUDA,
 5298             OpenCL, or Reference ]
 5299         engineGpuDeviceIndex, None [ Possible values: 0, 0 1, etc. ]
 5300 
 5301         Forcefield settings:
 5302 
 5303         forcefieldConstraints, HBonds  [ Possible values: HBonds,
 5304             AllBonds, or HAngles ]
 5305         forcefields, amber/ff14SB.xml amber/tip3p_standard.xml
 5306             amber/tip3p_HFE_multivalent.xml amber/phosaa10.xml
 5307             [ Possible values: A space delimited list of valid names. ]
 5308         forcefieldHydrogenMass, 3.0  [ Units: amu ]
 5309         forcefieldNonbondedCutoff, 0.9   [ Units: nanometer ]
 5310         forcefieldNonbondedMethod, PME  [ Possible values: PME or
 5311             NoCutoff ]
 5312         forcefieldRigidWater, yes,  [ Possible values: yes or no ]
 5313         forcefieldSmallMoleculeForcefield, openff-2.1.1  [ Possible
 5314             value: A valid forcefield name. ]
 5315 
 5316         Integrator settings:
 5317 
 5318         integratorBarostatFrequency, 25.0 * timestep  [ The specified value
 5319             is a multiple of integratorTimestep. ]
 5320         integratorConstraintTolerance, 1e-06
 5321         integratorLangevinCollisionRate, 1.0  [ Units: 1 / picosecond ]
 5322         integratorNRestartAttempts, 20
 5323         integratorReassignVelocities, no  [ Possible values: yes or no ]
 5324         integratorRemoveCom, no  [ Possible values: yes or no ]
 5325         integratorTimestep, 4.0 [ Units: femtosecond ]
 5326 
 5327         Partial charge settings:
 5328 
 5329         partialChargeNaglModel, None  [ Default: Production AM1BCC model for
 5330             NAGL; Possible value: Any valid name. ]
 5331         partialChargeNumberOfConformers, None  [ Possible value: > 0 ]
 5332         partialChargeOffToolkitBackend, AmberTools  [ Possible values:
 5333             AmberTools or RDKit ]
 5334         partialChargeMethod, AM1BCC  [ Possble values: AM1BCC, Espaloma,
 5335             or NAGL ]
 5336 
 5337         Restraint settings:
 5338 
 5339         restraintKPhiA, 334.72  [ Units: kilojoule_per_mole / radian**2
 5340             The default value is equivalent to 80 kcal/mol/radian**2 ]
 5341         restraintKPhiB, 334.72  [ Units: kilojoule_per_mole / radian**2 ]
 5342             The default value is equivalent to 80 kcal/mol/radian**2 ]
 5343         restraintKPhiC, 334.72  [ Units: kilojoule_per_mole / radian**2 ]
 5344             The default value is equivalent to 80 kcal/mo/radian**2 ]
 5345         restraintKR, 4184.0  [ Units: kilojoule_per_molel / nanometer**2
 5346                 The default value is equivalent to 10 kcal/mol/angstrom**2
 5347         restraintKThetaA, 334.72  [ Units: kilojoule_per_mole / radian**2 ]
 5348             The default value is equivalent to 80 kcal/mol/radian**2 ]
 5349         restraintKThetaB, 334.72  [ Units: kilojoule_per_mole / radian**2
 5350             The default value is equivalent to 80 kcal/mol/radian**2 ]
 5351         restraintAnchorFindingStrategy, bonded  [ Possible values:
 5352             multi-residue or bonded ]
 5353         restraintDsspFilter, yes   [ Possible values: yes or no ]
 5354         restraintHostMaxDistance, 1.5  [ Units: nanometer ]
 5355         restraintHostMinDistance, 0.5  [ Units: nanometer ]
 5356         restraintHostSelection, backbone   [ Possible value: Any valid
 5357             selection. ]
 5358         restraintRmsfCutoff, 0.1  [ Units: nanometer ]
 5359 
 5360         Solvent equil output settings:
 5361 
 5362         solventEquilOutputCheckpointInterval, 1.0  [ Units: nanosecond ]
 5363         solventEquilOutputCheckpointStorageFilename, checkpoint.chk
 5364         solventEquilEquilOutputNPTStructure, equil_npt_structure.pdb
 5365         solventEquilEquilNVTOutputStructure, equil_nvt_structure.pdb
 5366         solventEquilOutputForcefieldCache, db.json
 5367         solventEquilOutputLogOutput, production_equil_simulation.log
 5368         solventEquilOutputMinimizedStructure, minimized.pdb
 5369         solventEquilOutputIndices, all  [  Possible value: Any valid
 5370             selection. ]
 5371         solventEquilOutputPreminimizedStructure, system.pdb
 5372         solventEquilOutputProductionTrajectoryFilename, production_equil.xtc
 5373         solventEquilOutputTrajectoryWriteInterval, 20.0  [ Units:
 5374             picosecond ]
 5375 
 5376         Solvent_equil_simulation_settings:
 5377 
 5378         solventEquilSimulationEquilibrationLength, 0.2 [ Units: nanosecond ]
 5379         solventEquilSimulationEquilibrationLengthNVT, 0.1  [ Units:
 5380             nanosecond ]
 5381         solventEquilSimulationMinimizationSteps, 5000
 5382         solventEquilSimulationProductionLength, 0.5  [ Units: nanosecond ]
 5383 
 5384         Solvent lambda settings:
 5385 
 5386         solventLambdaElec, 0.0 0.25 0.5 0.75 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
 5387             1.0 1.0  [ Possible values: A space delimited list of values
 5388             between 0.0 and 1.0 ]
 5389         solventLambdaRestraints, 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
 5390             0.0 0.0 0.0  [ Possible values: A space delimited list of values
 5391             between 0.0 and 1.0 ]
 5392         solventLambdaVdw, 0.0 0.0 0.0 0.0 0.0 0.12 0.24 0.36 0.48 0.6 0.7
 5393             0.77 0.85 1.0  [ Possible values: A space delimited list of values
 5394             between 0.0 and 1.0 ]
 5395 
 5396         Solvent output settings:
 5397 
 5398         solventOutputCheckpointInterval, 1.0  [ Units: nanosecond ]
 5399         solventOutputCheckpointStorageFilename, solvent_checkpoint.nc
 5400         solventOutputForcefieldCache, db.json
 5401         solventOutputFilename, solvent.nc
 5402         solventOutputIndices, not water  [ Possible value: Any valid
 5403             selection. ]
 5404         solventOutputStructure, alchemical_system.pdb
 5405         solventOutputPositionsWriteFrequency, 100.0  [ Units: picosecond ]
 5406         solventOutputVelocitiesWriteFrequency, None  [ Possible
 5407             values: > 0; Units: picosecond ]
 5408 
 5409         Solvent simulation settings:
 5410 
 5411         solventSimulationEarlyTerminationTargetError, 0.0  [ Units:
 5412             kilocalorie_per_mole ]
 5413         solventSimulationEquilibrationLength, 1.0  [ Units: nanosecond ]
 5414         solventSimulationMinimizationSteps, 5000
 5415         solventSimulationNReplicas, 14
 5416         solventSimulationProductionLength, 10.0  [ Units: nanosecond ]
 5417         solventSimulationRealTimeAnalysisInterval, 250.0  [ Unit: picosecond ]
 5418         solventSimulationRealTimeAnalysisMinimumTime, 500.0  [ Units:
 5419             picosecond ]
 5420         solventSimulationSamplerMethod, repex  [ Possible values: repex,
 5421             sams, or independent ]
 5422         solventSimulationSamsFlatnessCriteria, logZ-flatness  [ Possible
 5423             values: logZ-flatness, minimum-visits or histogram-flatness ]
 5424         solventSimulationSamsGamma0, 1.0
 5425         solventSimulationTimePerIteration, 2.5  [ Units: picosecond ]
 5426 
 5427         Solvent solvation settings:
 5428 
 5429         solventSolvationBoxShape, dodecahedron  [  Possible values: cube,
 5430             dodecahedron, or octahedron ]
 5431         solventSolvationBoxSize, None  [ Possible value: A triplet of space
 5432             X Y Z values; Units: nanometer ]
 5433         solventSolvationSolventModel, tip3p  [ Possible values: tip3p, spce,
 5434             tip4pew, or tip5p ]
 5435         solventSolvationSolventPadding, 1.5  [ Units: nanometer ]
 5436 
 5437         Thermo settings:
 5438 
 5439         thermoPh, None  [ Possible values: > 0 ]
 5440         thermoPressure, 1.0  [ Units: bar ]
 5441         thermoRedoxPotential, None  [ Possible values: A valid float.
 5442             Units: millivolts (mV) ]
 5443         thermoTemperature, 298.15  [ Units: kelvin ]
 5444 
 5445     A brief description of parameters, taken from OpenFE documentation, is
 5446     provided below:
 5447 
 5448         protocolRepeats: Number of completely independent repeats of the
 5449             entire sampling process.
 5450 
 5451         Complex settings:
 5452 
 5453         Complex parameters for the system, including the solvent model and
 5454         the solvent padding.
 5455 
 5456         Complex equil output settings:
 5457 
 5458         Parameters controlling simulation output during equilibration
 5459         phase of complex transformation.
 5460 
 5461         complexEquilOutputCheckpointInterval: Frequency to write the
 5462             checkpoint file.
 5463         complexEquilOutputCheckpointStorageFilename: Checkpoint filename.
 5464         complexEquilOutputEquilNPTStructure: NPT structure filename.
 5465         complexEquilOutputEquilNVTstructure: NVT strucure filename.
 5466         complexEquilOutputForcefieldCache:  Filename for caching small
 5467             molecule residue templates.
 5468         complexEquilOutputLogOutput: Simulation log filename.
 5469         complexEquilOutputMinimizedStructure: Minimized structire filename.
 5470         complexEquilOutputIndices: Selection string for selecting
 5471             coordinates to write.
 5472         complexEquilOutputPremnimizedStructure: Initial structure filename.
 5473         complexEquilOutputProductionTrajectoryFilename: Trajectory filename.
 5474         complexEquilOutputTrajectoryWriteInterval: Frequency for writing
 5475             velocities to trajectory file.
 5476 
 5477         Complex equil simulation settings:
 5478 
 5479         Parameters controlling simulation during equilibration phase of
 5480         complex transformation.
 5481 
 5482         complexEquilSimulationEquilibrationLength:  Length of the NPT
 5483             equilibration phase.
 5484         complexEquilSimulationEquilibrationLengthNVT: Length of the NVT
 5485             equilibration phase.
 5486         complexEquilSimulationMinimizationSteps: Maximum number of
 5487             minimization steps to perform.
 5488         complexEquilSimulationProductionLength:  Length of the NPT
 5489             production phase.
 5490 
 5491         Complex lambda settings:
 5492 
 5493         Lambda protocol parameters for complex transformation.
 5494 
 5495         complexLambdaElec: List of lambda values for electrostatics. The
 5496             values of 0 and 1 imply state A and state B respectively.
 5497         complexLambdaRestraints: List of lambda values for restraints. The
 5498             values of 0 and 1 imply state A and state B respectively.
 5499         complexLambdaVdw: List of lamda values for van der Waals. The
 5500             values of of 0 and 1 imply state A and state B respectively.
 5501 
 5502         Complex output settings:
 5503 
 5504         Parameters controlling simulation output during final phase of
 5505         complex transformation.
 5506 
 5507         complexOutputCheckpointInterval:  Frequency to write the checkpoint
 5508             file.
 5509         complexOutputCheckpointStorageFilename: Checkpoint filename.
 5510         complexOutputForcefieldCache: Filename for caching small molecule
 5511             residue templates.
 5512         complexOutputFilename: Trajectory filename.
 5513         complexOutputIndices: Selection string for selecting coordinates to
 5514             write.
 5515         complexOutputStructure: Topology structure filename.
 5516         complexOutputPositionsWriteFrequency: Frequency for writing
 5517             positions to trajectory file.
 5518         complexOutputVelocitiesWriteFrequency: Frequency for writing
 5519             velocities to trajectory file.
 5520 
 5521         Complex simulation settings:
 5522 
 5523         Parameters controlling simulation during final phase of complex
 5524         transformation.
 5525 
 5526         complexSimulationEarlyTerminationTargetError: Target error for the
 5527             real time analysis measured in kcal/mol. Once the MBAR error of
 5528             the free energy is at or below this value, the simulation will
 5529             be considered complete. The suggested value of 0.12 has shown to
 5530             be effective in both hydration and binding free energy
 5531             benchmarks.
 5532         complexSimulationEquilibrationLength: Length of the equilibration
 5533             phase. The specified value must be divisible by
 5534             'integratorTimestep'.
 5535         complexSimulationMinimizationSteps: Maximum number of minimization
 5536             steps to perform.
 5537         complexSimulationNReplicas: Number of replicas to use.
 5538         complexSimulationProductionLength: Length of the production phase.
 5539             The specified value must be divisible by 'integratorTimestep'.
 5540         complexSimulationRealTimeAnalysisMinimumTime: Time interval for
 5541             performing analysis of the free energies. At each interval, real
 5542             time analysis data will be written to a yaml file named
 5543             <outputFileName>_real_time_analysis.yaml. The current error
 5544             in the estimate will also be assessed and the simulation will
 5545             be terminated when it drops below
 5546             'complexSimulationEarlyTerminationTargetError'.
 5547         complexSimulationSamplerMethod: Alchemical sampling method to use:
 5548             REPEX (Hamiltonian REPlica EXchange), SAMS (Self-Adjusted
 5549             Mixture Sampling), or Independent (Independently sampled lambda
 5550             windows).
 5551         complexSimulationSamsFlatnessCriteria:Method for assessing when to
 5552             switch to asymptomatically optimal scheme for SAMS.
 5553         complexSimulationsamsGamma0: Initial weight adaptation rate for
 5554             SAMS.
 5555         complexSimulationTimePerIteration: Simulation time between each
 5556             MCMC move attempt
 5557 
 5558         Complex solvation settings:
 5559 
 5560         Solvation parameters for the system, including the solvent model and
 5561         the solvent padding.
 5562 
 5563         complexSolvationBoxShape: Shape of the periodic solvent box.
 5564         complexSolvationBoxSize:  Lengths of the unit cell for a solvent box.
 5565         complexSolvationSolventModel: Forcefield water model to use during
 5566             solvation and defining the model properties.
 5567         complexSolvationSolventPadding: Minimum distance from any solute
 5568             bounding sphere to the edge of the box.
 5569 
 5570         Engine settings:
 5571 
 5572         Parameters configuring the compute platform used by the OpenMM to
 5573         perform the simulation.
 5574 
 5575         engineComputePlatform: Platform to use for running OpenMM MD
 5576             calculations.
 5577         engineGpuDeviceIndex: Space delimited list of device indices
 5578             to use for running OpenMM MD calculations.
 5579 
 5580         Forcefield settings:
 5581 
 5582         forcefieldConstraints:Constraints  to use.
 5583         forcefields: List of valid forcefield paths for all components
 5584             except small molecules.
 5585         forcefieldHydrogenMass: Mass to be repartitioned to hydrogens
 5586             from neighboring heavy atoms.
 5587         forcefieldNonbondedCutoff: Cutoff for short range nonbonded
 5588             interactions.
 5589         forcefieldNonbondedMethod: Method for treating nonbonded
 5590             interactions.
 5591         forcefieldRigidWater: Use a rigid water model.
 5592         forcefieldSmallMoleculeForcefield: A valid forcefield name to use
 5593             for small molecules.
 5594 
 5595         Integrator settings:
 5596 
 5597         Parameters controlling the LangevinSplittingDynamicsMove integrator
 5598         used for simulation.
 5599 
 5600         integratorBarostatFrequency: Frequency at which volume scaling
 5601             changes should be attempted.
 5602         integratorConstraintTolerance: Tolerance for constraint solver.
 5603         integratorLangevinCollisionRate: Collision frequency.
 5604         integratorNRestartAttempts: Number of attempts to restart from
 5605             Context in case there are NaNs in the energies after
 5606             integration.
 5607         integratorReassignVelocities: Reassign velocities  from the
 5608             Maxwell-Boltzmann distribution at the beginning of each
 5609             Monte Carlo move.
 5610         integratorRemoveCom: Remove the center of mass motion.
 5611         integratorTimestep: Size of the simulation timestep.
 5612 
 5613         Partial charge settings:
 5614 
 5615         Parameters for automatically assigning missing partial charges to
 5616         small molecules, including the partial charge method.
 5617 
 5618         partialChargeNaglModel: Model to use for partial charge assignment.
 5619             A value of None implies the use of the latest available
 5620             production AM1BCC model.
 5621         partialChargeNumberOfConformers: Number of conformers to generate
 5622             as part of the partial charge assignment. A value of None
 5623             implies the use of the existing conformer.
 5624         partialChargeOffToolkitBackend: OpenFF toolkit registry backend to
 5625             use for calculating partial charges.
 5626         partialChargeMethod: Method to use for calculating partial charges.
 5627 
 5628         Restraint settings:
 5629 
 5630         Parameters to configure Boresch-style restraint between two groups
 5631         of atoms named host  (Hx) and guest (Gx).
 5632 
 5633         restraintKPhiA: Equilibrium force constant for the dihedral formed
 5634             by H2-H1-H0-G0.
 5635         restraintKPhiB: Equilibrium force constant for the dihedral formed
 5636             by H1-H0-G0-G1.
 5637         restraintKPhiC: Equilibrium force constant for the dihedral formed
 5638             by H0-G0-G1-G2.
 5639         restraintKR: Bond spring constant between H0 and G0.
 5640         restraintKThetaA: Spring constant for the angle formed by H1-H0-G0.
 5641         restraintKThetaB: Spring constant for the angle formed by H0-G0-G1
 5642         restraintAnchorFindingStrategy: Boresch atom picking strategy to
 5643             use. bonded: pick host atoms that are bonded to each other.
 5644             multi-residue: pick host atoms which can span multiple
 5645             residues.
 5646         restraintDsspFilter: Apply DSSP filter to the host atoms.
 5647         restraintHostMaxDistance: Minimum distance between any host atom
 5648             and the guest G0 atom.
 5649         restraintHostMinDistance: Xaximum distance between any host atom
 5650             and the guest G0 atom
 5651         restraintHostSelection: A valid selection string to sub-select the
 5652             host atoms which will be involved in the restraint.
 5653         restraintRmsfCutoff: Cutoff value for filtering atoms by their root
 5654             mean square fluctuation. Atoms with values above this cutoff
 5655             are ignored.
 5656 
 5657         Solvent equil output settings:
 5658         Solvent equil simulation settings:
 5659         Solvent lambda settings:
 5660         Solvent output settings:
 5661         Solvent simulation settings:
 5662         Solvent solvation settings:
 5663 
 5664         The solvent settings are similar to the complex settings already
 5665         described under various sections for complex. The prefix 'solvent'
 5666         is used for the names of the pramaters instead of the prefix
 5667         'complex.'
 5668 
 5669         Thermo settings:
 5670 
 5671         Thermodynamic parameters, including the temperature and the pressure
 5672         of the system.
 5673 
 5674         thermoPh: Simulation pH
 5675         thermoPressure: Simulation pressure.
 5676         thermoRedoxPotential:Simulation redox potential.
 5677         thermoTemperature: Simulation temperature.
 5678 
 5679     Arguments:
 5680         ParamsOptionName (str): Command line OpenFE RFE parameters option name.
 5681         ParamsOptionValue (str): Comma delimited list of parameter name and value pairs.
 5682         ParamsDefaultInfo (dict): Default values to override selected parameters.
 5683 
 5684     Returns:
 5685         dictionary: Processed parameter name and value pairs.
 5686 
 5687     """
 5688 
 5689     ParamsInfo = _SetupAbsoluteBindingFreeEnergyDefaultParametersInfo(ParamsOptionName, ParamsOptionValue)
 5690 
 5691     (ValidParamNames, CanonicalParamNamesMap, ParamsOptionValue, ParamsOptionValueWords) = (
 5692         _ValidateAndCanonicalizeParameterNames(ParamsOptionName, ParamsOptionValue, ParamsInfo, ParamsDefaultInfo)
 5693     )
 5694 
 5695     if re.match("^auto$", ParamsOptionValue, re.I):
 5696         _ProcessOptionOpenFEAbsoluteBindingFreeEnergyParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 5697         return ParamsInfo
 5698 
 5699     for Index in range(0, len(ParamsOptionValueWords), 2):
 5700         Name = ParamsOptionValueWords[Index].strip()
 5701         Value = ParamsOptionValueWords[Index + 1].strip()
 5702 
 5703         ParamName = CanonicalParamNamesMap[Name.lower()]
 5704         ParamValue = Value
 5705 
 5706         if re.match(
 5707             "^(ProtocolRepeats|IntegratorNRestartAttempts|ComplexEquilSimulationMinimizationSteps|ComplexSimulationMinimizationSteps|ComplexSimulationNReplicas|IntegratorNRestartAttempts|PartialChargeNumberOfConformers|SolventEquilSimulationMinimizationSteps|SolventSimulationMinimizationSteps|SolventSimulationNReplicas)$",
 5708             ParamName,
 5709             re.I,
 5710         ):
 5711             #  Int > 0
 5712             if not MiscUtil.IsInteger(Value):
 5713                 MiscUtil.PrintError(
 5714                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 5715                     % (Value, ParamName, ParamsOptionName)
 5716                 )
 5717             Value = int(Value)
 5718             if Value <= 0:
 5719                 MiscUtil.PrintError(
 5720                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 5721                     % (ParamValue, ParamName, ParamsOptionName)
 5722                 )
 5723             ParamValue = Value
 5724         elif re.match(
 5725             "^(IntegratorConstraintTolerance|ComplexSimulationSamsGamma0|ForcefieldHydrogenMass|SolventSimulationSamsGamma0)$",
 5726             ParamName,
 5727             re.I,
 5728         ):
 5729             # float > 0
 5730             if not MiscUtil.IsFloat(Value):
 5731                 MiscUtil.PrintError(
 5732                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5733                     % (Value, ParamName, ParamsOptionName)
 5734                 )
 5735             Value = float(Value)
 5736             if Value <= 0:
 5737                 MiscUtil.PrintError(
 5738                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 5739                     % (ParamValue, ParamName, ParamsOptionName)
 5740                 )
 5741             ParamValue = Value
 5742         elif re.match(
 5743             "^(ForcefieldRigidWater|IntegratorReassignVelocities|IntegratorRemoveCom|RestraintDsspFilter)$",
 5744             ParamName,
 5745             re.I,
 5746         ):
 5747             #  bool
 5748             if not re.match("^(yes|no|true|false)$", Value, re.I):
 5749                 MiscUtil.PrintError(
 5750                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: yes or no'
 5751                     % (Value, Name, ParamsOptionName)
 5752                 )
 5753             ParamValue = True if re.match("^(yes|true)$", Value, re.I) else False
 5754         elif re.match("^ThermoPh$", ParamName, re.I):
 5755             #  float > 0 or None
 5756             if re.match("^None$", Value, re.I):
 5757                 ParamValue = None
 5758             else:
 5759                 if not MiscUtil.IsFloat(Value):
 5760                     MiscUtil.PrintError(
 5761                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5762                         % (Value, ParamName, ParamsOptionName)
 5763                     )
 5764                 Value = float(Value)
 5765                 if Value <= 0:
 5766                     MiscUtil.PrintError(
 5767                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 5768                         % (ParamValue, ParamName, ParamsOptionName)
 5769                     )
 5770                 ParamValue = Value
 5771         elif re.match("^ThermoRedoxPotential$", ParamName, re.I):
 5772             if re.match("^None$", Value, re.I):
 5773                 ParamValue = None
 5774             else:
 5775                 if not MiscUtil.IsFloat(Value):
 5776                     MiscUtil.PrintError(
 5777                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5778                         % (Value, ParamName, ParamsOptionName)
 5779                     )
 5780                 Value = float(Value)
 5781                 ParamValue = Value * openff.units.unit.millivolts
 5782         elif re.match("^IntegratorLangevinCollisionRate$", ParamName, re.I):
 5783             if not MiscUtil.IsFloat(Value):
 5784                 MiscUtil.PrintError(
 5785                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5786                     % (Value, ParamName, ParamsOptionName)
 5787                 )
 5788             Value = float(Value)
 5789             if Value <= 0:
 5790                 MiscUtil.PrintError(
 5791                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 5792                     % (ParamValue, ParamName, ParamsOptionName)
 5793                 )
 5794             ParamValue = Value / openff.units.unit.picosecond
 5795         elif re.match(
 5796             "^(ComplexLambdaElec|ComplexLambdaRestraints|ComplexLambdaVdw|SolventLambdaElec|SolventLambdaRestraints|SolventLambdaVdw)$",
 5797             ParamName,
 5798             re.I,
 5799         ):
 5800             # List of float values between 0 and 1...
 5801             Values = Value.split()
 5802             if len(Values) == 0:
 5803                 MiscUtil.PrintError(
 5804                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain a set of space delimited values\n'
 5805                     % (Value, ParamName, ParamsOptionName)
 5806                 )
 5807             for Value in Values:
 5808                 if not MiscUtil.IsFloat(Value):
 5809                     MiscUtil.PrintError(
 5810                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5811                         % (Value, ParamName, ParamsOptionName)
 5812                     )
 5813                 Value = float(Value)
 5814                 if Value < 0.0 or Value > 1.0:
 5815                     MiscUtil.PrintError(
 5816                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not valid value. Supported values: 0.0 to 1.0\n'
 5817                         % (Value, ParamName, ParamsOptionName)
 5818                     )
 5819             Values = [float(Value) for Value in Values]
 5820             ParamValue = Values
 5821         elif re.match("^IntegratorBarostatFrequency$", ParamName, re.I):
 5822             if not MiscUtil.IsFloat(Value):
 5823                 MiscUtil.PrintError(
 5824                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5825                     % (Value, ParamName, ParamsOptionName)
 5826                 )
 5827             Value = float(Value)
 5828             if Value <= 0:
 5829                 MiscUtil.PrintError(
 5830                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 5831                     % (ParamValue, ParamName, ParamsOptionName)
 5832                 )
 5833             ParamValue = Value * openff.units.unit.timestep
 5834         elif re.match("^IntegratorLangevinCollisionRate$", ParamName, re.I):
 5835             if not MiscUtil.IsFloat(Value):
 5836                 MiscUtil.PrintError(
 5837                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5838                     % (Value, ParamName, ParamsOptionName)
 5839                 )
 5840             Value = float(Value)
 5841             if Value <= 0:
 5842                 MiscUtil.PrintError(
 5843                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 5844                     % (ParamValue, ParamName, ParamsOptionName)
 5845                 )
 5846             ParamValue = Value / openff.units.unit.picosecond
 5847         elif re.match("^IntegratorTimestep$", ParamName, re.I):
 5848             # float > 0 femtosecond
 5849             if not MiscUtil.IsFloat(Value):
 5850                 MiscUtil.PrintError(
 5851                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5852                     % (Value, ParamName, ParamsOptionName)
 5853                 )
 5854             Value = float(Value)
 5855             if Value <= 0:
 5856                 MiscUtil.PrintError(
 5857                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 5858                     % (ParamValue, ParamName, ParamsOptionName)
 5859                 )
 5860             ParamValue = Value * openff.units.unit.femtosecond
 5861         elif re.match(
 5862             "^(ComplexEquilOutputTrajectoryWriteInterval|ComplexOutputPositionsWriteFrequency|ComplexSimulationRealTimeAnalysisInterval|ComplexSimulationRealTimeAnalysisMinimumTime|ComplexSimulationTimePerIteration|SolventEquilOutputTrajectoryWriteInterval|SolventOutputPositionsWriteFrequency|SolventSimulationRealTimeAnalysisInterval|SolventSimulationRealTimeAnalysisMinimumTime|SolventSimulationTimePerIteration)$",
 5863             ParamName,
 5864             re.I,
 5865         ):
 5866             #  float > 0 picosecond
 5867             if not MiscUtil.IsFloat(Value):
 5868                 MiscUtil.PrintError(
 5869                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5870                     % (Value, ParamName, ParamsOptionName)
 5871                 )
 5872             Value = float(Value)
 5873             if Value <= 0:
 5874                 MiscUtil.PrintError(
 5875                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 5876                     % (ParamValue, ParamName, ParamsOptionName)
 5877                 )
 5878             ParamValue = Value * openff.units.unit.picosecond
 5879         elif re.match(
 5880             "^(ComplexEquilOutputCheckpointInterval|ComplexEquilSimulationEquilibrationLength|ComplexEquilSimulationEquilibrationLengthNVT|ComplexEquilSimulationProductionLength|ComplexOutputCheckpointInterval|ComplexSimulationEquilibrationLength|ComplexSimulationProductionLength|SolventEquilOutputCheckpointInterval|SolventEquilSimulationEquilibrationLength|SolventEquilSimulationEquilibrationLengthNVT|SolventEquilSimulationProductionLength|SolventOutputCheckpointInterval|SolventSimulationEquilibrationLength|SolventSimulationProductionLength)$",
 5881             ParamName,
 5882             re.I,
 5883         ):
 5884             #  float > 0 nanosecond
 5885             if not MiscUtil.IsFloat(Value):
 5886                 MiscUtil.PrintError(
 5887                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5888                     % (Value, ParamName, ParamsOptionName)
 5889                 )
 5890             Value = float(Value)
 5891             if Value <= 0:
 5892                 MiscUtil.PrintError(
 5893                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 5894                     % (ParamValue, ParamName, ParamsOptionName)
 5895                 )
 5896             ParamValue = Value * openff.units.unit.nanosecond
 5897         elif re.match(
 5898             "^(ComplexOutputVelocitiesWriteFrequency|SolventOutputVelocitiesWriteFrequency)$", ParamName, re.I
 5899         ):
 5900             #  float > 0 picosecond or none
 5901             if re.match("^None$", Value, re.I):
 5902                 ParamValue = None
 5903             else:
 5904                 if not MiscUtil.IsFloat(Value):
 5905                     MiscUtil.PrintError(
 5906                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5907                         % (Value, ParamName, ParamsOptionName)
 5908                     )
 5909                 Value = float(Value)
 5910                 if Value <= 0:
 5911                     MiscUtil.PrintError(
 5912                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 5913                         % (ParamValue, ParamName, ParamsOptionName)
 5914                     )
 5915                 ParamValue = Value * openff.units.unit.picosecond
 5916         elif re.match("^PartialChargeMethod$", ParamName, re.I):
 5917             if not re.match("^(AM1BCC|AM1BCCELF10|Espaloma|NAGL)$", Value, re.I):
 5918                 MiscUtil.PrintError(
 5919                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: AM1BCC, AM1BCCELF10, Espaloma, or NAGL'
 5920                     % (Value, Name, ParamsOptionName)
 5921                 )
 5922             ParamValue = Value.lower()
 5923         elif re.match("^PartialChargeOffToolkitBackend$", ParamName, re.I):
 5924             if not re.match("^(AmberTools|OpenEye|RDKit)$", Value, re.I):
 5925                 MiscUtil.PrintError(
 5926                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: AmberTools, OpenEye, or RDKit'
 5927                     % (Value, Name, ParamsOptionName)
 5928                 )
 5929             ParamValue = Value.lower()
 5930         elif re.match("^(ComplexSolvationBoxShape|SolventSolvationBoxShape)$", ParamName, re.I):
 5931             if not re.match("^(cube|dodecahedron|octahedron)$", Value, re.I):
 5932                 MiscUtil.PrintError(
 5933                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: cube, dodecahedron, or octahedron'
 5934                     % (Value, Name, ParamsOptionName)
 5935                 )
 5936             ParamValue = Value.lower()
 5937         elif re.match("^(ComplexSolvationBoxSize|SolventSolvationBoxSize)$", ParamName, re.I):
 5938             # List of X, Y, Z values...
 5939             if re.match("^None$", Value, re.I):
 5940                 ParamValue = None
 5941             else:
 5942                 Values = Value.split()
 5943                 if len(Values) != 3:
 5944                     MiscUtil.PrintError(
 5945                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain a set of three space delimited values.\n'
 5946                         % (Value, ParamName, ParamsOptionName)
 5947                     )
 5948                 for Value in Values:
 5949                     if not MiscUtil.IsFloat(Value):
 5950                         MiscUtil.PrintError(
 5951                             'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5952                             % (Value, ParamName, ParamsOptionName)
 5953                         )
 5954                 Values = [float(Value) for Value in Values]
 5955                 ParamValue = Values * openff.units.unit.nanometer
 5956         elif re.match("^(ComplexSolvationSolventModel|SolventSolvationSolventModel)$", ParamName, re.I):
 5957             if not re.match("^(tip3p|spce|tip4pew|tip5p)$", Value, re.I):
 5958                 MiscUtil.PrintError(
 5959                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: tip3p, spce, tip4pew, or tip5p'
 5960                     % (Value, Name, ParamsOptionName)
 5961                 )
 5962             ParamValue = Value.lower()
 5963         elif re.match("^EngineComputePlatform$", ParamName, re.I):
 5964             if not re.match("^(CPU|CUDA|OpenCL|Reference)$", Value, re.I):
 5965                 MiscUtil.PrintError(
 5966                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: CPU, CUDA, OpenCL, or Reference'
 5967                     % (Value, Name, ParamsOptionName)
 5968                 )
 5969             ParamValue = Value
 5970         elif re.match(
 5971             "^(ForcefieldNonbondedCutoff|RestraintHostMaxDistance|RestraintHostMinDistance|RestraintRmsfCutoff)$",
 5972             ParamName,
 5973             re.I,
 5974         ):
 5975             #  float > 0 and units nanometer
 5976             if not MiscUtil.IsFloat(Value):
 5977                 MiscUtil.PrintError(
 5978                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5979                     % (Value, ParamName, ParamsOptionName)
 5980                 )
 5981             Value = float(Value)
 5982             if Value <= 0:
 5983                 MiscUtil.PrintError(
 5984                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 5985                     % (ParamValue, ParamName, ParamsOptionName)
 5986                 )
 5987             ParamValue = Value * openff.units.unit.nanometer
 5988         elif re.match("^(ComplexSolvationSolventPadding|SolventSolvationSolventPadding)$", ParamName, re.I):
 5989             #  float > 0 and units nanometer or none
 5990             if re.match("^None$", Value, re.I):
 5991                 ParamValue = None
 5992             else:
 5993                 if not MiscUtil.IsFloat(Value):
 5994                     MiscUtil.PrintError(
 5995                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 5996                         % (Value, ParamName, ParamsOptionName)
 5997                     )
 5998                 Value = float(Value)
 5999                 if Value <= 0:
 6000                     MiscUtil.PrintError(
 6001                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 6002                         % (ParamValue, ParamName, ParamsOptionName)
 6003                     )
 6004                 ParamValue = Value * openff.units.unit.nanometer
 6005         elif re.match("^EngineGpuDeviceIndex$", ParamName, re.I):
 6006             #  Comma delimited string values...
 6007             DeviceIndices = Value.split()
 6008             if len(DeviceIndices) == 0:
 6009                 MiscUtil.PrintError(
 6010                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain space delimited list of device indices.\n'
 6011                     % (Value, ParamName, ParamsOptionName)
 6012                 )
 6013             for DeviceIndex in DeviceIndices:
 6014                 if not MiscUtil.IsInteger(DeviceIndex):
 6015                     MiscUtil.PrintError(
 6016                         'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 6017                         % (DeviceIndex, ParamName, ParamsOptionName)
 6018                     )
 6019                 DeviceIndices = [int(DeviceIndex) for DeviceIndex in DeviceIndices]
 6020             ParamValue = DeviceIndices
 6021         elif re.match("^(ForcefieldConstraints)$", ParamName, re.I):
 6022             if not re.match("^(HBonds|AllBonds|HAngles|None)$", Value, re.I):
 6023                 MiscUtil.PrintError(
 6024                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: HBonds, AllBonds, HAngles, or None'
 6025                     % (Value, Name, ParamsOptionName)
 6026                 )
 6027             ParamValue = None if re.match("^None$", Value, re.I) else Value.lower()
 6028         elif re.match("^(Forcefields)$", ParamName, re.I):
 6029             #  List of string values.....
 6030             Values = Value.split()
 6031             if len(Values) == 0:
 6032                 MiscUtil.PrintError(
 6033                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. It must contain a space delimited list of values..\n'
 6034                     % (Value, ParamName, ParamsOptionName)
 6035                 )
 6036             ParamValue = Values
 6037         elif re.match("^(ForcefieldNonbondedMethod)$", ParamName, re.I):
 6038             if not re.match("^(PME|NoCutoff)$", Value, re.I):
 6039                 MiscUtil.PrintError(
 6040                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is may be a valid OpenFE value. Supported values: PME or NoCutoff'
 6041                     % (Value, Name, ParamsOptionName)
 6042                 )
 6043             ParamValue = Value.lower()
 6044         elif re.match(
 6045             "^(ComplexSimulationEarlyTerminationTargetError|SolventSimulationEarlyTerminationTargetError)$",
 6046             ParamName,
 6047             re.I,
 6048         ):
 6049             # float >= 0 units: kilocalorie_per_mole
 6050             if not MiscUtil.IsFloat(Value):
 6051                 MiscUtil.PrintError(
 6052                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 6053                     % (Value, ParamName, ParamsOptionName)
 6054                 )
 6055             Value = float(Value)
 6056             if Value < 0:
 6057                 MiscUtil.PrintError(
 6058                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 6059                     % (ParamValue, ParamName, ParamsOptionName)
 6060                 )
 6061             ParamValue = Value * openff.units.unit.kilocalorie_per_mole
 6062         elif re.match(
 6063             "^(RestraintKPhiA|RestraintKPhiB|RestraintKPhiC|RestraintKThetaA|RestraintKThetaB)$", ParamName, re.I
 6064         ):
 6065             # float > 0 units: kilojoule_per_mole / radian ** 2
 6066             if not MiscUtil.IsFloat(Value):
 6067                 MiscUtil.PrintError(
 6068                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 6069                     % (Value, ParamName, ParamsOptionName)
 6070                 )
 6071             Value = float(Value)
 6072             if Value <= 0:
 6073                 MiscUtil.PrintError(
 6074                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 6075                     % (ParamValue, ParamName, ParamsOptionName)
 6076                 )
 6077             ParamValue = Value * openff.units.unit.kilojoule_per_mole / openff.units.unit.radian**2
 6078         elif re.match("^(RestraintKR)$", ParamName, re.I):
 6079             # float > 0 units: kilojoule_per_mole / nanometer ** 2
 6080             if not MiscUtil.IsFloat(Value):
 6081                 MiscUtil.PrintError(
 6082                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 6083                     % (Value, ParamName, ParamsOptionName)
 6084                 )
 6085             Value = float(Value)
 6086             if Value <= 0:
 6087                 MiscUtil.PrintError(
 6088                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 6089                     % (ParamValue, ParamName, ParamsOptionName)
 6090                 )
 6091             ParamValue = Value * openff.units.unit.kilojoule_per_mole / openff.units.unit.nanometer**2
 6092         elif re.match("^RestraintAnchorFindingStrategy$", ParamName, re.I):
 6093             if not re.match("^(multi-residue|bonded)$", Value, re.I):
 6094                 MiscUtil.PrintError(
 6095                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: multi-residue or bonded'
 6096                     % (Value, Name, ParamsOptionName)
 6097                 )
 6098             ParamValue = Value
 6099         elif re.match("^(ComplexSimulationSamplerMethod|SolventSimulationSamplerMethod)$", ParamName, re.I):
 6100             if not re.match("^(repex|sams|independent)$", Value, re.I):
 6101                 MiscUtil.PrintError(
 6102                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: repex, sams, or independent'
 6103                     % (Value, Name, ParamsOptionName)
 6104                 )
 6105             ParamValue = Value.lower()
 6106         elif re.match(
 6107             "^(ComplexSimulationSamsFlatnessCriteria|SolventSimulationSamsFlatnessCriteria)$", ParamName, re.I
 6108         ):
 6109             if not re.match("^(logz-flatness|minimum-visits|histogram-flatness)$", Value, re.I):
 6110                 MiscUtil.PrintError(
 6111                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: logz-flatness, minimum-visits, or histogram-flatness'
 6112                     % (Value, Name, ParamsOptionName)
 6113                 )
 6114             ParamValue = Value.lower()
 6115         elif re.match("^ThermoPressure$", ParamName, re.I):
 6116             #  float > 0 and units standard_atmosphere
 6117             if not MiscUtil.IsFloat(Value):
 6118                 MiscUtil.PrintError(
 6119                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 6120                     % (Value, ParamName, ParamsOptionName)
 6121                 )
 6122             Value = float(Value)
 6123             if Value <= 0:
 6124                 MiscUtil.PrintError(
 6125                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 6126                     % (ParamValue, ParamName, ParamsOptionName)
 6127                 )
 6128             ParamValue = Value * openff.units.unit.bar
 6129         elif re.match("^ThermoTemperature$", ParamName, re.I):
 6130             # float >= 0 and units kelvin
 6131             if not MiscUtil.IsFloat(Value):
 6132                 MiscUtil.PrintError(
 6133                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 6134                     % (Value, ParamName, ParamsOptionName)
 6135                 )
 6136             Value = float(Value)
 6137             if Value < 0:
 6138                 MiscUtil.PrintError(
 6139                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: >= 0\n'
 6140                     % (ParamValue, ParamName, ParamsOptionName)
 6141                 )
 6142             ParamValue = Value * openff.units.unit.kelvin
 6143         else:
 6144             # Str or None...
 6145             ParamValue = None if re.match("^None$", Value, re.I) else Value
 6146 
 6147         # Set value...
 6148         ParamsInfo[ParamName] = ParamValue
 6149 
 6150     # Handle parameters with possible auto values...
 6151     _ProcessOptionOpenFEAbsoluteBindingFreeEnergyParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 6152 
 6153     return ParamsInfo
 6154 
 6155 
 6156 def _ProcessOptionOpenFEAbsoluteBindingFreeEnergyParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 6157     """Process parameters with possible auto values and perform validation."""
 6158 
 6159     for NamePrefix in ["Complex", "Solvent"]:
 6160         ParamName1 = "%sSolvationBoxSize" % NamePrefix
 6161         ParamValue1 = ParamsInfo[ParamName1]
 6162         ParamName2 = "%sSolvationSolventPadding" % NamePrefix
 6163         ParamValue2 = ParamsInfo[ParamName2]
 6164         if ParamsInfo[ParamName1] is not None and ParamsInfo[ParamName2] is not None:
 6165             MiscUtil.PrintError(
 6166                 'The parameter values, %s and %s, specified for parameter names, %s and %s, using "%s" option is not a valid value. You must specify only one of these values.\n'
 6167                 % (ParamValue1, ParamValue2, ParamName1, ParamName2, ParamsOptionName)
 6168             )
 6169 
 6170     for NamePrefix in ["Complex", "Solvent"]:
 6171         ParamName1 = "%sLambdaElec" % NamePrefix
 6172         ParamValue1Count = len(ParamsInfo[ParamName1])
 6173         ParamName2 = "%sLambdaRestraints" % NamePrefix
 6174         ParamValue2Count = len(ParamsInfo[ParamName2])
 6175         ParamName3 = "%sLambdaVdw" % NamePrefix
 6176         ParamValue3Count = len(ParamsInfo[ParamName3])
 6177         if ParamValue1Count != ParamValue2Count or ParamValue1Count != ParamValue3Count:
 6178             MiscUtil.PrintError(
 6179                 'The number of values - %s, %s, and %s - specified for parameter names - %s, %s, and %s, using "%s" option are not valid. You must specify same number of values for these parameters.'
 6180                 % (
 6181                     ParamValue1Count,
 6182                     ParamValue2Count,
 6183                     ParamValue3Count,
 6184                     ParamName1,
 6185                     ParamName2,
 6186                     ParamName3,
 6187                     ParamsOptionName,
 6188                 )
 6189             )
 6190 
 6191     _ProcessPartialChargeMethodAbsoluteBindingFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 6192     _ProcessPartialChargeNaglAbsoluteBindingFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 6193 
 6194 
 6195 def _ProcessPartialChargeMethodAbsoluteBindingFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 6196     """Process  PartialChargeMethod ABFE paramater."""
 6197 
 6198     _ProcessPartialChargeMethodFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 6199 
 6200 
 6201 def _ProcessPartialChargeNaglAbsoluteBindingFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 6202     """Process  PartialChargeNaglModel ABFE paramater."""
 6203 
 6204     _ProcessPartialChargeNaglFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 6205 
 6206 
 6207 def _SetupAbsoluteBindingFreeEnergyDefaultParametersInfo(ParamsOptionName, ParamsOptionValue):
 6208     """Setup ABFE default parameters information using the current ABFE settings."""
 6209 
 6210     ParamsInfo = {}
 6211 
 6212     from openfe.protocols.openmm_afe import AbsoluteBindingProtocol
 6213 
 6214     ABFESettings = AbsoluteBindingProtocol.default_settings()
 6215     ABFEParametersMap = _SetupMapForAbsoluteBindingFreeEnergyParameters()
 6216 
 6217     for ParamName in ABFEParametersMap.keys():
 6218         ABFEParamGroupName, ABFEParamName = ABFEParametersMap[ParamName]
 6219         if ABFEParamGroupName is None:
 6220             if hasattr(ABFESettings, ABFEParamName):
 6221                 ParamsInfo[ParamName] = getattr(ABFESettings, ABFEParamName)
 6222             else:
 6223                 MiscUtil.PrintInfo(
 6224                     'The OpenFE ABFE settings name, %s, corresponding to ABFE parameter name, %s, specified using option "%s" is not available. Ignoring parameter...'
 6225                     % (ABFEParamName, ParamName, ParamsOptionName)
 6226                 )
 6227         else:
 6228             ABFEParamGroupSettings = (
 6229                 getattr(ABFESettings, ABFEParamGroupName) if hasattr(ABFESettings, ABFEParamGroupName) else None
 6230             )
 6231             if ABFEParamGroupSettings is not None and hasattr(ABFEParamGroupSettings, ABFEParamName):
 6232                 ParamsInfo[ParamName] = getattr(ABFEParamGroupSettings, ABFEParamName)
 6233             else:
 6234                 MiscUtil.PrintInfo(
 6235                     'The OpenFE ABFE parameter name, %s, for settings, %s, corresponding to ABFE parameter name, %s, specified using option "%s" is not available. Ignoring parameter...'
 6236                     % (ABFEParamName, ABFEParamGroupName, ParamName, ParamsOptionName)
 6237                 )
 6238 
 6239     return ParamsInfo
 6240 
 6241 
 6242 def _SetupMapForAbsoluteBindingFreeEnergyParameters():
 6243     """Map relative free energy option paramater names to OpenFE absolute
 6244     binding free energy settings.
 6245     """
 6246 
 6247     ABFEParametersMap = {
 6248         "ProtocolRepeats": [None, "protocol_repeats"],
 6249         "ComplexEquilOutputCheckpointInterval": ["complex_equil_output_settings", "checkpoint_interval"],
 6250         "ComplexEquilOutputCheckpointStorageFilename": ["complex_equil_output_settings", "checkpoint_storage_filename"],
 6251         "ComplexEquilOutputEquilNPTStructure": ["complex_equil_output_settings", "equil_npt_structure"],
 6252         "ComplexEquilOutputEquilNVTstructure": ["complex_equil_output_settings", "equil_nvt_structure"],
 6253         "ComplexEquilOutputForcefieldCache": ["complex_equil_output_settings", "forcefield_cache"],
 6254         "ComplexEquilOutputLogOutput": ["complex_equil_output_settings", "log_output"],
 6255         "ComplexEquilOutputMinimizedStructure": ["complex_equil_output_settings", "minimized_structure"],
 6256         "ComplexEquilOutputIndices": ["complex_equil_output_settings", "output_indices"],
 6257         "ComplexEquilOutputPreminimizedStructure": ["complex_equil_output_settings", "preminimized_structure"],
 6258         "ComplexEquilOutputProductionTrajectoryFilename": [
 6259             "complex_equil_output_settings",
 6260             "production_trajectory_filename",
 6261         ],
 6262         "ComplexEquilOutputTrajectoryWriteInterval": ["complex_equil_output_settings", "trajectory_write_interval"],
 6263         "ComplexEquilSimulationEquilibrationLength": ["complex_equil_simulation_settings", "equilibration_length"],
 6264         "ComplexEquilSimulationEquilibrationLengthNVT": [
 6265             "complex_equil_simulation_settings",
 6266             "equilibration_length_nvt",
 6267         ],
 6268         "ComplexEquilSimulationMinimizationSteps": ["complex_equil_simulation_settings", "minimization_steps"],
 6269         "ComplexEquilSimulationProductionLength": ["complex_equil_simulation_settings", "production_length"],
 6270         "ComplexLambdaElec": ["complex_lambda_settings", "lambda_elec"],
 6271         "ComplexLambdaRestraints": ["complex_lambda_settings", "lambda_restraints"],
 6272         "ComplexLambdaVdw": ["complex_lambda_settings", "lambda_vdw"],
 6273         "ComplexOutputCheckpointInterval": ["complex_output_settings", "checkpoint_interval"],
 6274         "ComplexOutputCheckpointStorageFilename": ["complex_output_settings", "checkpoint_storage_filename"],
 6275         "ComplexOutputForcefieldCache": ["complex_output_settings", "forcefield_cache"],
 6276         "ComplexOutputFilename": ["complex_output_settings", "output_filename"],
 6277         "ComplexOutputIndices": ["complex_output_settings", "output_indices"],
 6278         "ComplexOutputStructure": ["complex_output_settings", "output_structure"],
 6279         "ComplexOutputPositionsWriteFrequency": ["complex_output_settings", "positions_write_frequency"],
 6280         "ComplexOutputVelocitiesWriteFrequency": ["complex_output_settings", "velocities_write_frequency"],
 6281         "ComplexSimulationEarlyTerminationTargetError": [
 6282             "complex_simulation_settings",
 6283             "early_termination_target_error",
 6284         ],
 6285         "ComplexSimulationEquilibrationLength": ["complex_simulation_settings", "equilibration_length"],
 6286         "ComplexSimulationMinimizationSteps": ["complex_simulation_settings", "minimization_steps"],
 6287         "ComplexSimulationNReplicas": ["complex_simulation_settings", "n_replicas"],
 6288         "ComplexSimulationProductionLength": ["complex_simulation_settings", "production_length"],
 6289         "ComplexSimulationRealTimeAnalysisInterval": ["complex_simulation_settings", "real_time_analysis_interval"],
 6290         "ComplexSimulationRealTimeAnalysisMinimumTime": [
 6291             "complex_simulation_settings",
 6292             "real_time_analysis_minimum_time",
 6293         ],
 6294         "ComplexSimulationSamplerMethod": ["complex_simulation_settings", "sampler_method"],
 6295         "ComplexSimulationSamsFlatnessCriteria": ["complex_simulation_settings", "sams_flatness_criteria"],
 6296         "ComplexSimulationSamsGamma0": ["complex_simulation_settings", "sams_gamma0"],
 6297         "ComplexSimulationTimePerIteration": ["complex_simulation_settings", "time_per_iteration"],
 6298         "ComplexSolvationBoxShape": ["complex_solvation_settings", "box_shape"],
 6299         "ComplexSolvationBoxSize": ["complex_solvation_settings", "box_size"],
 6300         "ComplexSolvationSolventModel": ["complex_solvation_settings", "solvent_model"],
 6301         "ComplexSolvationSolventPadding": ["complex_solvation_settings", "solvent_padding"],
 6302         "EngineComputePlatform": ["engine_settings", "compute_platform"],
 6303         "EngineGpuDeviceIndex": ["engine_settings", "gpu_device_index"],
 6304         "ForcefieldConstraints": ["forcefield_settings", "constraints"],
 6305         "Forcefields": ["forcefield_settings", "forcefields"],
 6306         "ForcefieldHydrogenMass": ["forcefield_settings", "hydrogen_mass"],
 6307         "ForcefieldNonbondedCutoff": ["forcefield_settings", "nonbonded_cutoff"],
 6308         "ForcefieldNonbondedMethod": ["forcefield_settings", "nonbonded_method"],
 6309         "ForcefieldRigidWater": ["forcefield_settings", "rigid_water"],
 6310         "ForcefieldSmallMoleculeForcefield": ["forcefield_settings", "small_molecule_forcefield"],
 6311         "IntegratorBarostatFrequency": ["integrator_settings", "barostat_frequency"],
 6312         "IntegratorConstraintTolerance": ["integrator_settings", "constraint_tolerance"],
 6313         "IntegratorLangevinCollisionRate": ["integrator_settings", "langevin_collision_rate"],
 6314         "IntegratorNRestartAttempts": ["integrator_settings", "n_restart_attempts"],
 6315         "IntegratorReassignVelocities": ["integrator_settings", "reassign_velocities"],
 6316         "IntegratorRemoveCom": ["integrator_settings", "remove_com"],
 6317         "IntegratorTimestep": ["integrator_settings", "timestep"],
 6318         "PartialChargeNaglModel": ["partial_charge_settings", "nagl_model"],
 6319         "PartialChargeNumberOfConformers": ["partial_charge_settings", "number_of_conformers"],
 6320         "PartialChargeOffToolkitBackend": ["partial_charge_settings", "off_toolkit_backend"],
 6321         "PartialChargeMethod": ["partial_charge_settings", "partial_charge_method"],
 6322         "RestraintKPhiA": ["restraint_settings", "K_phiA"],
 6323         "RestraintKPhiB": ["restraint_settings", "K_phiB"],
 6324         "RestraintKPhiC": ["restraint_settings", "K_phiC"],
 6325         "RestraintKR": ["restraint_settings", "K_r"],
 6326         "RestraintKThetaA": ["restraint_settings", "K_thetaA"],
 6327         "RestraintKThetaB": ["restraint_settings", "K_thetaB"],
 6328         "RestraintAnchorFindingStrategy": ["restraint_settings", "anchor_finding_strategy"],
 6329         "RestraintDsspFilter": ["restraint_settings", "dssp_filter"],
 6330         "RestraintHostMaxDistance": ["restraint_settings", "host_max_distance"],
 6331         "RestraintHostMinDistance": ["restraint_settings", "host_min_distance"],
 6332         "RestraintHostSelection": ["restraint_settings", "host_selection"],
 6333         "RestraintRmsfCutoff": ["restraint_settings", "rmsf_cutoff"],
 6334         "SolventEquilOutputCheckpointInterval": ["solvent_equil_output_settings", "checkpoint_interval"],
 6335         "SolventEquilOutputCheckpointStorageFilename": ["solvent_equil_output_settings", "checkpoint_storage_filename"],
 6336         "SolventEquilOutputEquilNPTStructure": ["solvent_equil_output_settings", "equil_npt_structure"],
 6337         "SolventEquilOutputEquilNVTstructure": ["solvent_equil_output_settings", "equil_nvt_structure"],
 6338         "SolventEquilOutputForcefieldCache": ["solvent_equil_output_settings", "forcefield_cache"],
 6339         "SolventEquilOutputLogOutput": ["solvent_equil_output_settings", "log_output"],
 6340         "SolventEquilOutputMinimizedStructure": ["solvent_equil_output_settings", "minimized_structure"],
 6341         "SolventEquilOutputIndices": ["solvent_equil_output_settings", "output_indices"],
 6342         "SolventEquilOutputPreminimizedStructure": ["solvent_equil_output_settings", "preminimized_structure"],
 6343         "SolventEquilOutputProductionTrajectoryFilename": [
 6344             "solvent_equil_output_settings",
 6345             "production_trajectory_filename",
 6346         ],
 6347         "SolventEquilOutputTrajectoryWriteInterval": ["solvent_equil_output_settings", "trajectory_write_interval"],
 6348         "SolventEquilSimulationEquilibrationLength": ["solvent_equil_simulation_settings", "equilibration_length"],
 6349         "SolventEquilSimulationEquilibrationLengthNVT": [
 6350             "solvent_equil_simulation_settings",
 6351             "equilibration_length_nvt",
 6352         ],
 6353         "SolventEquilSimulationMinimizationSteps": ["solvent_equil_simulation_settings", "minimization_steps"],
 6354         "SolventEquilSimulationProductionLength": ["solvent_equil_simulation_settings", "production_length"],
 6355         "SolventLambdaElec": ["solvent_lambda_settings", "lambda_elec"],
 6356         "SolventLambdaRestraints": ["solvent_lambda_settings", "lambda_restraints"],
 6357         "SolventLambdaVdw": ["solvent_lambda_settings", "lambda_vdw"],
 6358         "SolventOutputCheckpointInterval": ["solvent_output_settings", "checkpoint_interval"],
 6359         "SolventOutputCheckpointStorageFilename": ["solvent_output_settings", "checkpoint_storage_filename"],
 6360         "SolventOutputForcefieldCache": ["solvent_output_settings", "forcefield_cache"],
 6361         "SolventOutputFilename": ["solvent_output_settings", "output_filename"],
 6362         "SolventOutputIndices": ["solvent_output_settings", "output_indices"],
 6363         "SolventOutputStructure": ["solvent_output_settings", "output_structure"],
 6364         "SolventOutputPositionsWriteFrequency": ["solvent_output_settings", "positions_write_frequency"],
 6365         "SolventOutputVelocitiesWriteFrequency": ["solvent_output_settings", "velocities_write_frequency"],
 6366         "SolventSimulationEarlyTerminationTargetError": [
 6367             "solvent_simulation_settings",
 6368             "early_termination_target_error",
 6369         ],
 6370         "SolventSimulationEquilibrationLength": ["solvent_simulation_settings", "equilibration_length"],
 6371         "SolventSimulationMinimizationSteps": ["solvent_simulation_settings", "minimization_steps"],
 6372         "SolventSimulationNReplicas": ["solvent_simulation_settings", "n_replicas"],
 6373         "SolventSimulationProductionLength": ["solvent_simulation_settings", "production_length"],
 6374         "SolventSimulationRealTimeAnalysisInterval": ["solvent_simulation_settings", "real_time_analysis_interval"],
 6375         "SolventSimulationRealTimeAnalysisMinimumTime": [
 6376             "solvent_simulation_settings",
 6377             "real_time_analysis_minimum_time",
 6378         ],
 6379         "SolventSimulationSamplerMethod": ["solvent_simulation_settings", "sampler_method"],
 6380         "SolventSimulationSamsFlatnessCriteria": ["solvent_simulation_settings", "sams_flatness_criteria"],
 6381         "SolventSimulationSamsGamma0": ["solvent_simulation_settings", "sams_gamma0"],
 6382         "SolventSimulationTimePerIteration": ["solvent_simulation_settings", "time_per_iteration"],
 6383         "SolventSolvationBoxShape": ["solvent_solvation_settings", "box_shape"],
 6384         "SolventSolvationBoxSize": ["solvent_solvation_settings", "box_size"],
 6385         "SolventSolvationSolventModel": ["solvent_solvation_settings", "solvent_model"],
 6386         "SolventSolvationSolventPadding": ["solvent_solvation_settings", "solvent_padding"],
 6387         "ThermoPh": ["thermo_settings", "ph"],
 6388         "ThermoPressure": ["thermo_settings", "pressure"],
 6389         "ThermoRedoxPotential": ["thermo_settings", "redox_potential"],
 6390         "ThermoTemperature": ["thermo_settings", "temperature"],
 6391     }
 6392 
 6393     return ABFEParametersMap
 6394 
 6395 
 6396 def _UpdateOpenFESettings(FECalcType, ParamsOptionName, ParamsInfo, FESettings, FEParametersMap):
 6397     """Update OpenFE settings using values corresponding to  parameter names."""
 6398 
 6399     for ParamName in FEParametersMap.keys():
 6400         FEParamGroupName, FEParamName = FEParametersMap[ParamName]
 6401         if FEParamGroupName is None:
 6402             if hasattr(FESettings, FEParamName):
 6403                 try:
 6404                     setattr(FESettings, FEParamName, ParamsInfo[ParamName])
 6405                 except Exception as ErrMsg:
 6406                     MiscUtil.PrintInfo(
 6407                         '\nThe OpenFE %s settings name, %s, corresponding to %s parameter name, %s, specified using option "%s" is not settable:\n\n%s\n'
 6408                         % (FECalcType, FEParamName, FECalcType, ParamName, ParamsOptionName, ErrMsg)
 6409                     )
 6410             else:
 6411                 MiscUtil.PrintInfo(
 6412                     '\nThe OpenFE %s settings name, %s, corresponding to %s parameter name, %s, specified using option "%s" is not available. Ignoring parameter...'
 6413                     % (FECalcType, FEParamName, FECalcType, ParamName, ParamsOptionName)
 6414                 )
 6415         else:
 6416             FEaramGroupSettings = (
 6417                 getattr(FESettings, FEParamGroupName) if hasattr(FESettings, FEParamGroupName) else None
 6418             )
 6419             if FEaramGroupSettings is not None and hasattr(FEaramGroupSettings, FEParamName):
 6420                 try:
 6421                     setattr(FEaramGroupSettings, FEParamName, ParamsInfo[ParamName])
 6422                 except Exception as ErrMsg:
 6423                     MiscUtil.PrintInfo(
 6424                         '\nThe OpenFE %s settings name, %s, corresponding to %s parameter name, %s, specified using option "%s" is not settable:\n\n%s\n'
 6425                         % (FECalcType, FEParamName, FECalcType, ParamName, ParamsOptionName, ErrMsg)
 6426                     )
 6427             else:
 6428                 MiscUtil.PrintInfo(
 6429                     '\nThe OpenFE %s parameter name, %s, for settings, %s, corresponding to %s parameter name, %s, specified using option "%s" is not available. Ignoring parameter...'
 6430                     % (FECalcType, FEParamName, FECalcType, FEParamGroupName, ParamName, ParamsOptionName)
 6431                 )
 6432 
 6433 
 6434 def _ProcessPartialChargeMethodFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 6435     """Process  PartialChargeMethod paramater."""
 6436 
 6437     ParamName = "PartialChargeMethod"
 6438     ParamValue = ParamsInfo[ParamName]
 6439     if re.match("^Espaloma$", ParamValue, re.I):
 6440         if not _IsEspalomaChargeModuleAvailable():
 6441             MiscUtil.PrintError(
 6442                 'The parameter value, %s specified for parameter name, %s, using "%s" option is not a valid value.  Espaloma module is not available in your environment.\n'
 6443                 % (ParamValue, ParamName, ParamsOptionName)
 6444             )
 6445     elif re.match("^NAGL$", ParamValue, re.I):
 6446         if not _IsNAGLChargeModuleAvailable():
 6447             MiscUtil.PrintError(
 6448                 'The parameter value, %s specified for parameter name, %s, using "%s" option is not a valid value.  NAGL module is not available in your environment.\n'
 6449                 % (ParamValue, ParamName, ParamsOptionName)
 6450             )
 6451 
 6452 
 6453 def _ProcessPartialChargeNaglFreeEnergyParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 6454     """Process  PartialChargeNaglModel paramater."""
 6455 
 6456     ParamName = "PartialChargeMethod"
 6457     ParamValue = ParamsInfo[ParamName]
 6458     if not re.match("^NAGL$", ParamValue, re.I):
 6459         return
 6460 
 6461     if not _IsNAGLChargeModuleAvailable():
 6462         MiscUtil.PrintError(
 6463             'The parameter value, %s specified for parameter name, %s, using "%s" option is not a valid value.  NAGL module is not available in your environment.\n'
 6464             % (ParamValue, ParamName, ParamsOptionName)
 6465         )
 6466 
 6467     ParamName = "PartialChargeNaglModel"
 6468     ParamValue = ParamsInfo[ParamName]
 6469     _CheckAvailabilityOfNAGLModels(ParamName, ParamValue, ParamsOptionName)
 6470 
 6471 
 6472 def ProcessOptionOpenFESolventParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo=None):
 6473     """Process parameters for solvation option and return a map containing
 6474     processed parameter names and values.
 6475 
 6476     The ParamsOptionValue is a comma delimited list of parameter name and value
 6477     pairs to setup solvation parameters for creating OpenFE SolventComponent.
 6478 
 6479     The supported parameter names along with their default and possible
 6480     values are shown below:
 6481 
 6482         positiveIon, Na+ [ Possible value: Li+, Na+, K+, Rb+, or Cs+ ]
 6483         negativeIon, Cl- [ Possible values: Cl-, Br-, F-, or I- ]
 6484         neutralize, yes  [ Possible values: yes or no ]
 6485         ionConcentration, 0.15  [ Units: molar ]
 6486 
 6487     A brief description of parameters is provided below:
 6488 
 6489         positiveIon, negativeion: Pair of ions used to neutralize and bring
 6490             the solvent to required ionic concentration.
 6491         neutralize: Neutralize the net charge on the chemical state by the
 6492             ions in the solvent component.
 6493         ionConcentration: Ionic concentration.
 6494 
 6495     Arguments:
 6496         ParamsOptionName (str): Command line solvation parameters option name.
 6497         ParamsOptionValue (str): Comma delimited list of parameter name and value pairs.
 6498         ParamsDefaultInfo (dict): Default values to override for selected parameters.
 6499 
 6500     Returns:
 6501         dictionary: Processed parameter name and value pairs.
 6502 
 6503     """
 6504 
 6505     ParamsInfo = {"PositiveIon": "Na+", "NegativeIon": "Cl-", "Neutralize": True, "IonConcentration": 0.15}
 6506 
 6507     (ValidParamNames, CanonicalParamNamesMap, ParamsOptionValue, ParamsOptionValueWords) = (
 6508         _ValidateAndCanonicalizeParameterNames(ParamsOptionName, ParamsOptionValue, ParamsInfo, ParamsDefaultInfo)
 6509     )
 6510 
 6511     if re.match("^auto$", ParamsOptionValue, re.I):
 6512         _ProcessOptionOpenFESolventParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 6513         return ParamsInfo
 6514 
 6515     for Index in range(0, len(ParamsOptionValueWords), 2):
 6516         Name = ParamsOptionValueWords[Index].strip()
 6517         Value = ParamsOptionValueWords[Index + 1].strip()
 6518 
 6519         ParamName = CanonicalParamNamesMap[Name.lower()]
 6520         ParamValue = Value
 6521 
 6522         if re.match("^PositiveIon$", ParamName, re.I):
 6523             ValidValues = "Li+ Na+ K+ Rb+ Cs+"
 6524             EscapedValidValuesPattern = r"Li\+|Na\+|K\+|Rb\+|Cs\+"
 6525             if not re.match("^(%s)$" % EscapedValidValuesPattern, Value):
 6526                 MiscUtil.PrintError(
 6527                     'The value specified, %s, for parameter name, %s, using  "%s" option is not a valid.  Supported value(s): %s'
 6528                     % (ParamValue, ParamName, ParamsOptionName, ValidValues)
 6529                 )
 6530             ParamValue = Value
 6531         elif re.match("^NegativeIon$", ParamName, re.I):
 6532             ValidValues = "F- Cl- Br- I-"
 6533             ValidValuesPattern = "F-|Cl-|Br-|I-"
 6534             if not re.match("^(%s)$" % ValidValuesPattern, Value):
 6535                 MiscUtil.PrintError(
 6536                     'The value specified, %s, for parameter name, %s, using  "%s" option is not a valid.  Supported value(s): %s'
 6537                     % (ParamValue, ParamName, ParamsOptionName, ValidValues)
 6538                 )
 6539             ParamValue = Value
 6540         elif re.match("^IonConcentration$", ParamName, re.I):
 6541             if not MiscUtil.IsFloat(Value):
 6542                 MiscUtil.PrintError(
 6543                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 6544                     % (Value, ParamName, ParamsOptionName)
 6545                 )
 6546             Value = float(Value)
 6547             if Value < 0:
 6548                 MiscUtil.PrintError(
 6549                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 6550                     % (ParamValue, ParamName, ParamsOptionName)
 6551                 )
 6552             ParamValue = Value
 6553         elif re.match("^Neutralize$", ParamName, re.I):
 6554             if not re.match("^(yes|no|true|false)$", Value, re.I):
 6555                 MiscUtil.PrintError(
 6556                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: yes or no'
 6557                     % (Value, Name, ParamsOptionName)
 6558                 )
 6559             ParamValue = True if re.match("^(yes|true)$", Value, re.I) else False
 6560         else:
 6561             ParamValue = Value
 6562 
 6563         # Set value...
 6564         ParamsInfo[ParamName] = ParamValue
 6565 
 6566     # Handle parameters with possible auto values...
 6567     _ProcessOptionOpenFESolventParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 6568 
 6569     return ParamsInfo
 6570 
 6571 
 6572 def _ProcessOptionOpenFESolventParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 6573     """Process parameters with possible auto values and perform validation."""
 6574 
 6575     # Setup units for IonConcentration...
 6576     ParamName = "IonConcentration"
 6577     ParamValue = ParamsInfo[ParamName]
 6578     if MiscUtil.IsNumber(ParamValue):
 6579         ParamsInfo[ParamName] = ParamValue * openff.units.unit.molar
 6580 
 6581 
 6582 def ProcessOptionOpenFEMapper(OptionName, OptionValue):
 6583     """Process mapper command line option and return a list of valid mapper
 6584     names.
 6585 
 6586     Valid atom mapper names are: LOMAP or Kartograf
 6587 
 6588     Arguments:
 6589         OptionName (str): Command line mapper option name.
 6590         OptionValue (str): Comma delimited lis of mapper option values.
 6591 
 6592     Returns:
 6593         list: List of valid canonical mapper names.
 6594 
 6595     """
 6596 
 6597     MapperList = []
 6598     Mappers = OptionValue.strip()
 6599     for MapperName in Mappers.split(","):
 6600         MapperName = MapperName.strip()
 6601         if re.match("^LOMAP$", MapperName, re.I):
 6602             MapperList.append("LOMAP")
 6603         elif re.match("^Kartograf$", MapperName, re.I):
 6604             MapperList.append("Kartograf")
 6605         else:
 6606             MiscUtil.PrintError(
 6607                 'The value specified, %s, for option "%s" is not valid. Supported values: LOMAP or Kartograf'
 6608                 % (MapperName, OptionName)
 6609             )
 6610 
 6611     return MapperList
 6612 
 6613 
 6614 def ProcessOptionOpenFEMapperParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo=None):
 6615     """Process parameters for mapper option and return a map containing processed
 6616     parameter names and values.
 6617 
 6618     The ParamsOptionValue is a comma delimited list of parameter name and value pairs
 6619     to setup platform.
 6620 
 6621     The supported parameter names along with their default and possible
 6622     values are shown below:
 6623 
 6624         lomapTime, 20, [ Units: seconds ]
 6625         lomapThreeD, yes [ Possible values: yes or no ]
 6626         lomapMax3D, 1.0 [ Units: Angstrom ]
 6627         lomapElementChange, yes [ Possible values: yes or no]
 6628         lomapSeed, None [ Possible value: A string. An empty string causes
 6629             MCS search to start from scratch ]
 6630         lomapShift, no [  Possible values: yes or no]
 6631 
 6632         kartografAtomMaxDistance, 0.95 [ Units: Angstrom ]
 6633         kartografAtomMapHydrogens, yes [ Possible values: yes or no ]
 6634         kartografMapHydrogensOnHydrogensOnly, No [ Possible values: yes or
 6635             no ]
 6636         kartografMapExactRingMatchesOnly, yes [ Possible values: yes or no ]
 6637         kartografAllowPartialFusedRings, yes [ Possible values: yes or no ]
 6638 
 6639     A brief description of parameters is provided below:
 6640 
 6641         lomapTime: Time out for MCS algorithm.
 6642         lomapThreeD: Use atom positions to prune symmetric mappings.
 6643         lomapMax3D: Forbid mapping between atoms with distance more than
 6644             specified value.
 6645         lomapElementChange: Allow mappings that change an atom element.
 6646         lomapSeed: An Empty SMARTS string causes MCS search to start from
 6647             scratch.
 6648         lomapShift: Keep pre-aligned atom positions for 3D position checks.
 6649 
 6650         kartografAtomMaxDistance: Geometric criteria for two atoms
 6651             corresponding to maximum distance between them.
 6652         kartografAtomMapHydrogens: Map hydrogens.
 6653         kartografMapHydrogensOnHydrogensOnly: Map hydrogens only on
 6654             hydrogens.
 6655         kartografMapExactRingMatchesOnly: Map rings with only matching ring
 6656             size and bond orders. In addition, ring breaking is not permitted.
 6657         kartografAllowPartialFusedRings: Allow mapping of partially fused
 6658             rings.
 6659 
 6660     Arguments:
 6661         ParamsOptionName (str): Command line OpenFE mapper parameters option name.
 6662         ParamsOptionValue (str): Comma delimited list of parameter name and value pairs.
 6663         ParamsDefaultInfo (dict): Default values to override for selected parameters.
 6664 
 6665     Returns:
 6666         dictionary: Processed parameter name and value pairs.
 6667 
 6668     """
 6669 
 6670     ParamsInfo = {
 6671         "LomapTime": 20,
 6672         "LomapThreeD": True,
 6673         "LomapMax3D": 1.0,
 6674         "LomapElementChange": True,
 6675         "LomapSeed": None,
 6676         "LomapShift": False,
 6677         "KartografAtomMaxDistance": 0.95,
 6678         "KartografAtomMapHydrogens": True,
 6679         "KartografMapHydrogensOnHydrogensOnly": False,
 6680         "KartografMapExactRingMatchesOnly": True,
 6681         "KartografAllowPartialFusedRings": True,
 6682     }
 6683 
 6684     (ValidParamNames, CanonicalParamNamesMap, ParamsOptionValue, ParamsOptionValueWords) = (
 6685         _ValidateAndCanonicalizeParameterNames(ParamsOptionName, ParamsOptionValue, ParamsInfo, ParamsDefaultInfo)
 6686     )
 6687 
 6688     if re.match("^auto$", ParamsOptionValue, re.I):
 6689         _ProcessOptionOpenFEMapperParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 6690         return ParamsInfo
 6691 
 6692     for Index in range(0, len(ParamsOptionValueWords), 2):
 6693         Name = ParamsOptionValueWords[Index].strip()
 6694         Value = ParamsOptionValueWords[Index + 1].strip()
 6695 
 6696         ParamName = CanonicalParamNamesMap[Name.lower()]
 6697         ParamValue = Value
 6698 
 6699         if re.match("^(LomapMax3D|KartografAtomMaxDistance)$", ParamName, re.I):
 6700             if not MiscUtil.IsFloat(Value):
 6701                 MiscUtil.PrintError(
 6702                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 6703                     % (Value, ParamName, ParamsOptionName)
 6704                 )
 6705             Value = float(Value)
 6706             if Value <= 0:
 6707                 MiscUtil.PrintError(
 6708                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 6709                     % (ParamValue, ParamName, ParamsOptionName)
 6710                 )
 6711             ParamValue = Value
 6712         elif re.match("^(LomapTime)$", ParamName, re.I):
 6713             if not MiscUtil.IsInteger(Value):
 6714                 MiscUtil.PrintError(
 6715                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 6716                     % (Value, ParamName, ParamsOptionName)
 6717                 )
 6718             Value = int(Value)
 6719             if Value <= 0:
 6720                 MiscUtil.PrintError(
 6721                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 6722                     % (ParamValue, ParamName, ParamsOptionName)
 6723                 )
 6724             ParamValue = Value
 6725         elif re.match(
 6726             "^(LomapThreeD|LomapElementChange|LomapShift|KartografAtomMapHydrogens|KartografMapHydrogensOnHydrogensOnly|KartografMapExactRingMatchesOnly|KartografAllowPartialFusedRings)$",
 6727             ParamName,
 6728             re.I,
 6729         ):
 6730             if not re.match("^(yes|no|true|false)$", Value, re.I):
 6731                 MiscUtil.PrintError(
 6732                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: yes or no'
 6733                     % (Value, Name, ParamsOptionName)
 6734                 )
 6735             ParamValue = True if re.match("^(yes|true)$", Value, re.I) else False
 6736         else:
 6737             ParamValue = Value
 6738 
 6739         # Set value...
 6740         ParamsInfo[ParamName] = ParamValue
 6741 
 6742     # Handle parameters with possible auto values...
 6743     _ProcessOptionOpenFEMapperParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue)
 6744 
 6745     return ParamsInfo
 6746 
 6747 
 6748 def _ProcessOptionOpenFEMapperParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue):
 6749     """Process parameters with possible auto values and perform validation."""
 6750 
 6751     ParamName = "LomapSeed"
 6752     ParamValue = ParamsInfo[ParamName]
 6753     if ParamValue is None or re.match("^None$", ParamValue, re.I):
 6754         ParamsInfo[ParamName] = ""
 6755 
 6756 
 6757 def ProcessOptionOpenFECharge(OptionName, OptionValue):
 6758     """Process charge command line option and return a valid canonical
 6759     charge method name.
 6760 
 6761     Valid network names are: AM1BCC, AM1-Mulliken, Espaloma, Gasteiger, MMFF94
 6762     or NAGL
 6763 
 6764     Arguments:
 6765         OptionName (str): Command line charge option name.
 6766         OptionValue (str): Command line charge option value.
 6767 
 6768     Returns:
 6769         str: Canonical charge method name.
 6770 
 6771     """
 6772 
 6773     Value = OptionValue.strip()
 6774     if re.match("^AM1BCC$", Value, re.I):
 6775         Value = "AM1BCC"
 6776     elif re.match("^AM1-Mulliken$", Value, re.I):
 6777         Value = "AM1-Mulliken"
 6778     elif re.match("^Espaloma$", Value, re.I):
 6779         Value = "Espaloma"
 6780         if not _IsEspalomaChargeModuleAvailable():
 6781             MiscUtil.PrintError(
 6782                 'The value specified, %s, for option "%s" is not valid. Espaloma module is not available in your environment.'
 6783                 % (OptionValue, OptionName)
 6784             )
 6785     elif re.match("^Gasteiger$", Value, re.I):
 6786         Value = "Gasteiger"
 6787     elif re.match("^MMFF94$", Value, re.I):
 6788         Value = "MMFF94"
 6789     elif re.match("^NAGL$", Value, re.I):
 6790         Value = "NAGL"
 6791         if not _IsNAGLChargeModuleAvailable():
 6792             MiscUtil.PrintError(
 6793                 'The value specified, %s, for option "%s" is not valid. NAGL module is not available in your environment.'
 6794                 % (OptionValue, OptionName)
 6795             )
 6796     else:
 6797         MiscUtil.PrintError(
 6798             'The value specified, %s, for option "%s" is not valid. Supported values: AM1BCC, AM1-Mulliken, Espaloma, Gasteiger, MMFF94 or NAGL'
 6799             % (OptionValue, OptionName)
 6800         )
 6801 
 6802     return Value
 6803 
 6804 
 6805 def _IsEspalomaChargeModuleAvailable():
 6806     """Check for the availability of Espaloma charge module."""
 6807 
 6808     Status = False if importlib.util.find_spec("espaloma_charge") is None else True
 6809 
 6810     return Status
 6811 
 6812 
 6813 def _IsNAGLChargeModuleAvailable():
 6814     """Check for the availability of NAGL charge module."""
 6815 
 6816     Status = False if importlib.util.find_spec("openff.nagl_models") is None else True
 6817 
 6818     return Status
 6819 
 6820 
 6821 def ProcessOptionOpenFEChargeParameters(ParamsOptionName, ParamsOptionValue, ChargeMethod, ParamsDefaultInfo=None):
 6822     """Process parameters for charge option and return a map containing processed
 6823     parameter names and values.
 6824 
 6825     The ParamsOptionValue is a comma delimited list of parameter name and value pairs
 6826     to setup platform.
 6827 
 6828     The supported parameter names along with their default and possible
 6829     values are shown below:
 6830 
 6831         naglModel, auto [ Possible value: A valid NAGL model name. By
 6832             default, it corresponds to the latest AM1BCC production model ]
 6833         toolkit, auto [ Possible values: RDKit or AmberTools. Default value:
 6834             RDKit for Gasteiger and MMFF94; AmberTools for AM1BCC and
 6835             AM1-Mulliken; Not used for Espaloma and NAGL. ]
 6836 
 6837         numProcessors, 1  [ Only used for AM1BCC, AM1-Mulliken, Espaloma,
 6838             and NAGL ]
 6839 
 6840         precision, 4
 6841         lineSize, 90
 6842 
 6843         useConformer, auto  [ Use current conformer. Possible values: yes or
 6844             no. Default value: no for Gasteiger using AmberToolkit;
 6845             otherwise, yes. ]
 6846 
 6847     A brief description of parameters is provided below:
 6848 
 6849         naglModel: NAGL model name. The latest AM1BCC NAGL production
 6850             model is used by default. You must specify it explicitly in case no
 6851             production model is available.
 6852         toolkit: Toolkit name. RDKit for Gasteiger and MMFF94; AmberTools
 6853             for AM1BCC, AM1-Mulliken, and Gasteiger.
 6854 
 6855         numProcessors: Number of processors. This is only used during the
 6856             calculation of AM1BCC, AM1-Mulliken, Espaloma, and NAGL
 6857             employing OpenFE method bulk_assign_partial_charges().
 6858 
 6859         precision: Floating point precision for writing the calculated
 6860             partial atomic charges.
 6861         lineSize: Line size for writing the calculated partial aromic
 6862             charges to SD file as a string value for data field label
 6863             'atom.dprop.PartialCharge'.
 6864 
 6865         useConformer: Use current conformer. The current conformer is
 6866             always used to calculate AM1BCC, Espaloma abd NAGL charges
 6867             using OpenFE method bulk_assign_partial_charges() and this
 6868             option is ignored. In addition, the option value is passed to
 6869             OpenFF method assign_partial_charges() during the calculation
 6870             of AM1-Mulliken, Gasteiger and MMFF94 charges employing
 6871             AmberTools or RDKit. The RDKit functions, however, ignore the
 6872             conformer during the calculation of Gasteiger and MMFF94
 6873             charges. The current conformer appears not used to calculate
 6874             Gasteiger charges employing AmberTools.
 6875 
 6876     Arguments:
 6877         ParamsOptionName (str): Command line OpenFE network parameters option name.
 6878         ParamsOptionValue (str): Comma delimited list of parameter name and value pairs.
 6879         ChargeMethod (str): Charge method name.
 6880         ParamsDefaultInfo (dict): Default values to override for selected parameters.
 6881 
 6882     Returns:
 6883         dictionary: Processed parameter name and value pairs.
 6884 
 6885     """
 6886 
 6887     ParamsInfo = {
 6888         "NaglModel": "auto",
 6889         "Toolkit": "auto",
 6890         "UseConformer": "auto",
 6891         "NumProcessors": 1,
 6892         "Precision": 4,
 6893         "LineSize": 90,
 6894     }
 6895 
 6896     (ValidParamNames, CanonicalParamNamesMap, ParamsOptionValue, ParamsOptionValueWords) = (
 6897         _ValidateAndCanonicalizeParameterNames(ParamsOptionName, ParamsOptionValue, ParamsInfo, ParamsDefaultInfo)
 6898     )
 6899 
 6900     if re.match("^auto$", ParamsOptionValue, re.I):
 6901         _ProcessOptionOpenFEChargeParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue, ChargeMethod)
 6902         return ParamsInfo
 6903 
 6904     for Index in range(0, len(ParamsOptionValueWords), 2):
 6905         Name = ParamsOptionValueWords[Index].strip()
 6906         Value = ParamsOptionValueWords[Index + 1].strip()
 6907 
 6908         ParamName = CanonicalParamNamesMap[Name.lower()]
 6909         ParamValue = Value
 6910 
 6911         if re.match("^(NumProcessors|Precision|LineSize)$", ParamName, re.I):
 6912             if not MiscUtil.IsInteger(Value):
 6913                 MiscUtil.PrintError(
 6914                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 6915                     % (Value, ParamName, ParamsOptionName)
 6916                 )
 6917             Value = int(Value)
 6918             if Value <= 0:
 6919                 MiscUtil.PrintError(
 6920                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 6921                     % (ParamValue, ParamName, ParamsOptionName)
 6922                 )
 6923             ParamValue = Value
 6924         elif re.match("^Toolkit$", ParamName, re.I):
 6925             if not re.match("^auto$", Value, re.I):
 6926                 if not re.match("^(AmberTools|RDKit)$", Value, re.I):
 6927                     MiscUtil.PrintError(
 6928                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: AmberTools or RDKit'
 6929                         % (Value, Name, ParamsOptionName)
 6930                     )
 6931             ParamValue = Value
 6932         elif re.match("^UseConformer$", ParamName, re.I):
 6933             if not re.match("^auto$", Value, re.I):
 6934                 if not re.match("^(yes|no|true|false)$", Value, re.I):
 6935                     MiscUtil.PrintError(
 6936                         'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: yes or no'
 6937                         % (Value, Name, ParamsOptionName)
 6938                     )
 6939             ParamValue = Value
 6940         else:
 6941             ParamValue = Value
 6942 
 6943         # Set value...
 6944         ParamsInfo[ParamName] = ParamValue
 6945 
 6946     # Handle parameters with possible auto values...
 6947     _ProcessOptionOpenFEChargeParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue, ChargeMethod)
 6948 
 6949     return ParamsInfo
 6950 
 6951 
 6952 def _ProcessOptionOpenFEChargeParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue, ChargeMethod):
 6953     """Process parameters with possible auto values and perform validation."""
 6954 
 6955     _ProcessToolkitChargeParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue, ChargeMethod)
 6956     _ProcessNaglModelChargeParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue, ChargeMethod)
 6957     _ProcessUseConformerChargeParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue, ChargeMethod)
 6958 
 6959 
 6960 def _ProcessToolkitChargeParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue, ChargeMethod):
 6961     """Process Toolkit Charge parameter."""
 6962 
 6963     ParamName = "Toolkit"
 6964     ParamValue = ParamsInfo[ParamName]
 6965     if re.match("^auto$", ParamValue, re.I):
 6966         ParamValue = "RDKit" if re.match("^(Gasteiger|MMFF94)$", ChargeMethod, re.I) else "AmberTools"
 6967     ParamsInfo[ParamName] = ParamValue
 6968 
 6969     if re.match("^MMFF94$", ChargeMethod, re.I):
 6970         if not re.match("^RDKit$", ParamValue, re.I):
 6971             MiscUtil.PrintError(
 6972                 'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values for charge method %s: RDKit\n'
 6973                 % (ParamValue, ParamName, ParamsOptionName, ChargeMethod)
 6974             )
 6975 
 6976     if re.match("^(AM1BCC|AM1-Mulliken)$", ChargeMethod, re.I):
 6977         if not re.match("^AmberTools$", ParamValue, re.I):
 6978             MiscUtil.PrintError(
 6979                 'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values for charge method %s: AmberTools\n'
 6980                 % (ParamValue, ParamName, ParamsOptionName, ChargeMethod)
 6981             )
 6982 
 6983 
 6984 def _ProcessUseConformerChargeParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue, ChargeMethod):
 6985     """Process UseConformer charge parameter."""
 6986 
 6987     ParamName = "UseConformer"
 6988     ParamValue = "%s" % ParamsInfo[ParamName]
 6989 
 6990     if re.match("^auto$", ParamValue, re.I):
 6991         ParamValue = None
 6992         if re.match("^(AM1BCC|AM1-Mulliken|Espaloma|NAGL)$", ChargeMethod, re.I):
 6993             ParamValue = True
 6994         elif re.match("Gasteiger", ChargeMethod, re.I):
 6995             if re.match("^AmberTools$", ParamsInfo["Toolkit"], re.I):
 6996                 ParamValue = False
 6997             elif re.match("^RDKit$", ParamsInfo["Toolkit"], re.I):
 6998                 ParamValue = True
 6999         elif re.match("MMFF94", ChargeMethod, re.I):
 7000             if re.match("^RDKit$", ParamsInfo["Toolkit"], re.I):
 7001                 ParamValue = True
 7002         ParamsInfo[ParamName] = ParamValue
 7003 
 7004 
 7005 def _ProcessNaglModelChargeParameter(ParamsInfo, ParamsOptionName, ParamsOptionValue, ChargeMethod):
 7006     """Process NaglModel charge parameter."""
 7007 
 7008     if not re.match("^NAGL$", ChargeMethod, re.I):
 7009         return
 7010 
 7011     if not _IsNAGLChargeModuleAvailable():
 7012         MiscUtil.PrintError(
 7013             'The value specified, , for option "-c, --charge" is not valid. NAGL module is not available in your environment.'
 7014             % (ChargeMethod)
 7015         )
 7016 
 7017     ParamName = "NaglModel"
 7018     ParamValue = ParamsInfo[ParamName]
 7019     if re.match("^auto$", ParamValue, re.I):
 7020         ParamValue = None
 7021         ParamsInfo[ParamName] = None
 7022 
 7023     _CheckAvailabilityOfNAGLModels(ParamName, ParamValue, ParamsOptionName)
 7024 
 7025 
 7026 def _CheckAvailabilityOfNAGLModels(ParamName, ParamValue, ParamsOptionName):
 7027     """Check availability of NAGL models."""
 7028 
 7029     # Check for the availability of production models...
 7030     ModelType = "am1bcc"
 7031     ProductionOnly = True
 7032     AvailableModels = openff.nagl_models.get_models_by_type(model_type=ModelType, production_only=ProductionOnly)
 7033     if len(AvailableModels) >= 1:
 7034         if ParamValue is None:
 7035             # It would be automatically picked up by OpenFE NAGL charge calculation....
 7036             return
 7037 
 7038     # Check for the availability of all models...
 7039     ProductionOnly = False
 7040     AvailableModels = openff.nagl_models.get_models_by_type(model_type=ModelType, production_only=ProductionOnly)
 7041     AvailableModelNames = []
 7042     for ModelPath in AvailableModels:
 7043         DirName, ModelName = os.path.split(ModelPath)
 7044         AvailableModelNames.append(ModelName)
 7045 
 7046     if ParamValue is None:
 7047         MiscUtil.PrintWarning(
 7048             'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. No AM1BCC production models available for charge method NAGL. You must explicitly specify a NAGL model name. Possible values: %s\n'
 7049             % (ParamValue, ParamName, ParamsOptionName, " ".join(AvailableModelNames))
 7050         )
 7051     else:
 7052         if ParamValue not in AvailableModelNames:
 7053             MiscUtil.PrintWarning(
 7054                 'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Available models for charge method NAGL: %s\n'
 7055                 % (ParamValue, ParamName, ParamsOptionName, " ".join(AvailableModelNames))
 7056             )
 7057 
 7058 
 7059 def ProcessOptionOpenFENetwork(OptionName, OptionValue):
 7060     """Process network command line option and return a valid canonical
 7061     network name.
 7062 
 7063     Valid network names are: LOMAP, MinimalSpanning, or Radial.
 7064 
 7065     Arguments:
 7066         OptionName (str): Command line network option name.
 7067         OptionValue (str): Command line network option value.
 7068 
 7069     Returns:
 7070         str: Canonical network name.
 7071 
 7072     """
 7073 
 7074     Value = OptionValue.strip()
 7075     if re.match("^LOMAP$", Value, re.I):
 7076         Value = "LOMAP"
 7077     elif re.match("^MinimalSpanning$", Value, re.I):
 7078         Value = "MinimalSpanning"
 7079     elif re.match("^Radial$", Value, re.I):
 7080         Value = "Radial"
 7081     else:
 7082         MiscUtil.PrintError(
 7083             'The value specified, %s, for option "%s" is not valid. Supported values: LOMAP, MinimalSpanning or Radial'
 7084             % (OptionValue, OptionName)
 7085         )
 7086 
 7087     return Value
 7088 
 7089 
 7090 def ProcessOptionOpenFENetworkParameters(
 7091     ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo=None, RadialNetworkStatus=False
 7092 ):
 7093     """Process parameters for network option and return a map containing processed
 7094     parameter names and values.
 7095 
 7096     The ParamsOptionValue is a comma delimited list of parameter name and value pairs
 7097     to setup platform.
 7098 
 7099     The supported parameter names along with their default and possible
 7100     values are shown below:
 7101 
 7102         lomapDistanceCutoff, 0.4
 7103         lomapMaxPathLength, 6
 7104         lomapRequireCycleCovering, yes  [ Possible values: yes or no ]
 7105 
 7106         minimalSpanningProgress, no  [ Possible values: yes or no ]
 7107 
 7108         radialCentralLigand, None  [ Possible values: Valid ligand name ]
 7109 
 7110         outputEdges, no  [ Possible values: yes or no ]
 7111         outputNetworkFormat, svg  [ Possible values: Any valid format. ]
 7112 
 7113     A brief description of parameters is provided below:
 7114 
 7115         lomapDistanceCutoff: Maximum distance/dissimilarity between two
 7116             molecules for an edge to be accepted.
 7117         lomapMaxPathLength: Maximum distance between any two molecules in
 7118             the resulting network
 7119         lomapRequireCycleCovering: Add cycles into the network
 7120 
 7121         minimalSpanningProgress: Show progress using tqdm.
 7122 
 7123         radialCentralLigand: Name of central ligand. A valid ligand name
 7124             must be specified to generate a radial ligand network.
 7125 
 7126         outputEdges: Generate PNG image files for all edges in a ligand
 7127             network.
 7128         outputNetworkFormat: Valid image file format for ligand network.
 7129             You must specify a valid format supported by Python module
 7130             Matplotlib. For example: PNG (.png), SVG (.svg), PDF (.pdf),
 7131             etc. In addition, the graphml file is always generated.
 7132 
 7133     Arguments:
 7134         ParamsOptionName (str): Command line OpenFE network parameters option name.
 7135         ParamsOptionValue (str): Comma delimited list of parameter name and value pairs.
 7136         ParamsDefaultInfo (dict): Default values to override for selected parameters.
 7137         RadialNetworkStatus (bool): Radial network status.
 7138 
 7139     Returns:
 7140         dictionary: Processed parameter name and value pairs.
 7141 
 7142     """
 7143 
 7144     ParamsInfo = {
 7145         "LomapDistanceCutoff": 0.4,
 7146         "LomapMaxPathLength": 6,
 7147         "LomapRequireCycleCovering": True,
 7148         "MinimalSpanningProgress": False,
 7149         "RadialCentralLigand": None,
 7150         "OutputEdges": False,
 7151         "OutputNetworkFormat": "svg",
 7152     }
 7153 
 7154     (ValidParamNames, CanonicalParamNamesMap, ParamsOptionValue, ParamsOptionValueWords) = (
 7155         _ValidateAndCanonicalizeParameterNames(ParamsOptionName, ParamsOptionValue, ParamsInfo, ParamsDefaultInfo)
 7156     )
 7157 
 7158     if re.match("^auto$", ParamsOptionValue, re.I):
 7159         _ProcessOptionOpenFENetworkParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue, RadialNetworkStatus)
 7160         return ParamsInfo
 7161 
 7162     for Index in range(0, len(ParamsOptionValueWords), 2):
 7163         Name = ParamsOptionValueWords[Index].strip()
 7164         Value = ParamsOptionValueWords[Index + 1].strip()
 7165 
 7166         ParamName = CanonicalParamNamesMap[Name.lower()]
 7167         ParamValue = Value
 7168 
 7169         if re.match("^(LomapDistanceCutoff)$", ParamName, re.I):
 7170             if not MiscUtil.IsFloat(Value):
 7171                 MiscUtil.PrintError(
 7172                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be a float.\n'
 7173                     % (Value, ParamName, ParamsOptionName)
 7174                 )
 7175             Value = float(Value)
 7176             if Value <= 0:
 7177                 MiscUtil.PrintError(
 7178                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 7179                     % (ParamValue, ParamName, ParamsOptionName)
 7180                 )
 7181             ParamValue = Value
 7182         elif re.match("^(LomapMaxPathLength)$", ParamName, re.I):
 7183             if not MiscUtil.IsInteger(Value):
 7184                 MiscUtil.PrintError(
 7185                     'The parameter value, %s, specified for parameter name, %s, using "%s" option must be an integer.\n'
 7186                     % (Value, ParamName, ParamsOptionName)
 7187                 )
 7188             Value = int(Value)
 7189             if Value <= 0:
 7190                 MiscUtil.PrintError(
 7191                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: > 0\n'
 7192                     % (ParamValue, ParamName, ParamsOptionName)
 7193                 )
 7194             ParamValue = Value
 7195         elif re.match("^(LomapRequireCycleCovering|MinimalSpanningProgress|OutputEdges)$", ParamName, re.I):
 7196             if not re.match("^(yes|no|true|false)$", Value, re.I):
 7197                 MiscUtil.PrintError(
 7198                     'The parameter value, %s, specified for parameter name, %s, using "%s" option is not a valid value. Supported values: yes or no'
 7199                     % (Value, Name, ParamsOptionName)
 7200                 )
 7201             ParamValue = True if re.match("^(yes|true)$", Value, re.I) else False
 7202         else:
 7203             ParamValue = Value
 7204 
 7205         # Set value...
 7206         ParamsInfo[ParamName] = ParamValue
 7207 
 7208     # Handle parameters with possible auto values...
 7209     _ProcessOptionOpenFENetworkParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue, RadialNetworkStatus)
 7210 
 7211     return ParamsInfo
 7212 
 7213 
 7214 def _ProcessOptionOpenFENetworkParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue, RadialNetworkStatus):
 7215     """Process parameters with possible auto values and perform validation."""
 7216 
 7217     if RadialNetworkStatus:
 7218         ParamName = "RadialCentralLigand"
 7219         ParamValue = ParamsInfo[ParamName]
 7220         if ParamValue is None or MiscUtil.IsEmpty(ParamValue):
 7221             MiscUtil.PrintError(
 7222                 'The value specified, %s, for parameter name, %s, using option "%s" is not valid. You must specify a valid ligand name to generate a radial network.'
 7223                 % (ParamValue, ParamName, ParamsOptionName)
 7224             )
 7225 
 7226 
 7227 def _ValidateAndCanonicalizeParameterNames(ParamsOptionName, ParamsOptionValue, ParamsInfo, ParamsDefaultInfo):
 7228     """Validate and canonicalize parameter names."""
 7229 
 7230     # Setup a canonical paramater names...
 7231     ValidParamNames = []
 7232     CanonicalParamNamesMap = {}
 7233     for ParamName in sorted(ParamsInfo):
 7234         ValidParamNames.append(ParamName)
 7235         CanonicalParamNamesMap[ParamName.lower()] = ParamName
 7236 
 7237     # Update default values...
 7238     if ParamsDefaultInfo is not None:
 7239         for ParamName in ParamsDefaultInfo:
 7240             if ParamName not in ParamsInfo:
 7241                 MiscUtil.PrintError(
 7242                     'The default parameter name, %s, specified using "%s" option is not a valid name. Supported parameter names: %s'
 7243                     % (ParamName, ParamsDefaultInfo, " ".join(ValidParamNames))
 7244                 )
 7245             ParamsInfo[ParamName] = ParamsDefaultInfo[ParamName]
 7246 
 7247     ParamsOptionValue = ParamsOptionValue.strip()
 7248     if not ParamsOptionValue:
 7249         MiscUtil.PrintError('No valid parameter name and value pairs specified using "%s" option' % ParamsOptionName)
 7250 
 7251     ParamsOptionValueWords = None
 7252     if not re.match("^auto$", ParamsOptionValue, re.I):
 7253         ParamsOptionValueWords = ParamsOptionValue.split(",")
 7254         if len(ParamsOptionValueWords) % 2:
 7255             MiscUtil.PrintError(
 7256                 'The number of comma delimited paramater names and values, %d, specified using "%s" option must be an even number.'
 7257                 % (len(ParamsOptionValueWords), ParamsOptionName)
 7258             )
 7259 
 7260     if ParamsOptionValueWords is not None:
 7261         for Index in range(0, len(ParamsOptionValueWords), 2):
 7262             Name = ParamsOptionValueWords[Index].strip()
 7263             CanonicalName = Name.lower()
 7264             if CanonicalName not in CanonicalParamNamesMap:
 7265                 MiscUtil.PrintError(
 7266                     'The parameter name, %s, specified using "%s" is not a valid name. Supported parameter names: %s'
 7267                     % (Name, ParamsOptionName, " ".join(ValidParamNames))
 7268                 )
 7269 
 7270     return (ValidParamNames, CanonicalParamNamesMap, ParamsOptionValue, ParamsOptionValueWords)