예제 #1
0
파일: run.py 프로젝트: jchodera/yank
def dispatch(args):
    from yank.yank import Yank # TODO: Fix this awkward import syntax.

    store_directory = args['--store']

    # Set override options.
    options = dict()

    # Configure MPI, if requested.
    mpicomm = None
    if args['--mpi']:
        mpicomm = utils.initialize_mpi()
        logger.info("Initialized MPI on %d processes." % (mpicomm.size))

    # Configure logger
    utils.config_root_logger(args['--verbose'], mpicomm=mpicomm,
                             log_file_path=os.path.join(store_directory, 'run.log'))

    if args['--iterations']:
        options['number_of_iterations'] = int(args['--iterations'])
    if args['--online-analysis']:
        options['online_analysis'] = True
    if args['--platform'] not in [None, 'None']:
        options['platform'] = openmm.Platform.getPlatformByName(args['--platform'])
    if args['--precision']:
        # We need to modify the Platform object.
        if args['--platform'] is None:
            raise Exception("The --platform argument must be specified in order to specify platform precision.")

        # Set platform precision.
        precision = args['--precision']
        platform_name = args['--platform']
        logger.info("Setting %s platform to use precision model '%s'." % (platform_name, precision))
        if precision is not None:
            if platform_name == 'CUDA':
                options['platform'].setPropertyDefaultValue('CudaPrecision', precision)
            elif platform_name == 'OpenCL':
                options['platform'].setPropertyDefaultValue('OpenCLPrecision', precision)
            elif platform_name == 'CPU':
                if precision != 'mixed':
                    raise Exception("CPU platform does not support precision model '%s'; only 'mixed' is supported." % precision)
            elif platform_name == 'Reference':
                if precision != 'double':
                    raise Exception("Reference platform does not support precision model '%s'; only 'double' is supported." % precision)
            else:
                raise Exception("Platform selection logic is outdated and needs to be updated to add platform '%s'." % platform_name)

    # Create YANK object associated with data storage directory.
    yank = Yank(store_directory, mpicomm=mpicomm, **options)

    # Set YANK to resume from the store file.
    phases = None # By default, resume from all phases found in store_directory
    if args['--phase']: phases=[args['--phase']]
    yank.resume(phases=phases)

    # Run simulation.
    yank.run()

    return True
예제 #2
0
파일: analyze.py 프로젝트: jchodera/yank
def dispatch(args):
    utils.config_root_logger(args['--verbose'])

    if args['extract-trajectory']:
        return dispatch_extract_trajectory(args)

    analyze.analyze(args['--store'])
    return True
예제 #3
0
def dispatch(args):
    utils.config_root_logger(args['--verbose'])

    if args['extract-trajectory']:
        return dispatch_extract_trajectory(args)

    analyze.analyze(args['--store'])
    return True
예제 #4
0
파일: test_yank.py 프로젝트: jchodera/yank
    output = "\n"
    output += "Analytical binding free energy                                  : %10.5f +- %10.5f kT\n" % (binding_free_energy / kT, 0)
    output += "Computed binding free energy (with standard state correction)   : %10.5f +- %10.5f kT (nsigma = %3.1f)\n" % (Delta_f, dDelta_f, nsigma)
    output += "Computed binding free energy (without standard state correction): %10.5f +- %10.5f kT (nsigma = %3.1f)\n" % (Delta_f + standard_state_correction, dDelta_f, nsigma)
    output += "Standard state correction alone                                 : %10.5f           kT\n" % (standard_state_correction)
    print output

    #if (nsigma > NSIGMA_MAX):
    #    output += "\n"
    #    output += "Computed binding free energy differs from true binding free energy.\n"
    #    raise Exception(output)

    return [Delta_f, dDelta_f]

if __name__ == '__main__':
    from yank import utils
    utils.config_root_logger(True, log_file_path='test_LennardJones_pair.log')

    box_width_nsigma_values = np.array([3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0])
    Delta_f_n = list()
    dDelta_f_n = list()
    for (n, box_width_nsigma) in enumerate(box_width_nsigma_values):
        [Delta_f, dDelta_f] = notest_LennardJonesPair(box_width_nsigma=box_width_nsigma)
        Delta_f_n.append(Delta_f)
        dDelta_f_n.append(dDelta_f)
    Delta_f_n = np.array(Delta_f_n)
    dDelta_f_n = np.array(dDelta_f_n)

    for (box_width_nsigma, Delta_f, dDelta_f) in zip(box_width_nsigma_values, Delta_f_n, dDelta_f_n):
        print "%8.3f %12.6f %12.6f" % (box_width_nsigma, Delta_f, dDelta_f)
예제 #5
0
if not os.path.exists(workdir):
    os.makedirs(workdir)
    logger.info("Creating path %s" % workdir)

# Create directory to store files in.
outdir = os.path.join(os.getcwd(), store_dir)
if not os.path.exists(outdir):
    os.makedirs(outdir)
    logger.info("Creating path %s" % outdir)

# ==============================================================================
# CONFIGURE LOGGER
# ==============================================================================

from yank import utils
utils.config_root_logger(verbose, log_file_path=os.path.join(setup_dir, 'prepare.log'))

# ==============================================================================
# PREPARE STRUCTURE
# ==============================================================================

from pdbfixer import PDBFixer

is_periodic = (nonbonded_method not in [app.NoCutoff, app.CutoffNonPeriodic])

# ==============================================================================
# Retrieve the PDB file
# ==============================================================================

if pdb_filename:
    logger.info("Retrieving PDB '%s'..." % pdb_filename)
예제 #6
0
파일: prepare.py 프로젝트: akapoor85/yank
def dispatch_binding(args):
    """
    Set up a binding free energy calculation.

    Parameters
    ----------
    args : dict
       Command-line arguments from docopt.

    """

    verbose = args['--verbose']
    store_dir = args['--store']
    utils.config_root_logger(verbose,
                             log_file_path=os.path.join(
                                 store_dir, 'prepare.log'))

    #
    # Determine simulation options.
    #

    # Specify thermodynamic parameters.
    temperature = process_unit_bearing_argument(args, '--temperature',
                                                unit.kelvin)
    pressure = process_unit_bearing_argument(args, '--pressure',
                                             unit.atmospheres)
    thermodynamic_state = ThermodynamicState(temperature=temperature,
                                             pressure=pressure)

    # Create systems according to specified setup/import method.
    if args['amber']:
        [phases, systems, positions, atom_indices] = setup_binding_amber(args)
    elif args['gromacs']:
        [phases, systems, positions,
         atom_indices] = setup_binding_gromacs(args)
    else:
        logger.error(
            "No valid binding free energy calculation setup command specified: Must be one of ['amber', 'systembuilder']."
        )
        # Trigger help argument to be returned.
        return False

    # Report some useful properties.
    if verbose:
        if 'complex-explicit' in atom_indices:
            phase = 'complex-explicit'
        else:
            phase = 'complex-implicit'
        logger.info("TOTAL ATOMS      : %9d" %
                    len(atom_indices[phase]['complex']))
        logger.info("receptor         : %9d" %
                    len(atom_indices[phase]['receptor']))
        logger.info("ligand           : %9d" %
                    len(atom_indices[phase]['ligand']))
        if phase == 'complex-explicit':
            logger.info("solvent and ions : %9d" %
                        len(atom_indices[phase]['solvent']))

    # Initialize YANK object.
    yank = Yank(store_dir)

    # Set options.
    options = dict()
    if args['--nsteps']:
        options['nsteps_per_iteration'] = int(args['--nsteps'])
    if args['--iterations']:
        options['number_of_iterations'] = int(args['--iterations'])
    if args['--equilibrate']:
        options['number_of_equilibration_iterations'] = int(
            args['--equilibrate'])
    if args['--online-analysis']:
        options['online_analysis'] = True
    if args['--restraints']:
        yank.restraint_type = args['--restraints']
    if args['--randomize-ligand']:
        options['randomize_ligand'] = True
    if args['--minimize']:
        options['minimize'] = True

    # Allow platform to be optionally specified in order for alchemical tests to be carried out.
    if args['--platform'] not in [None, 'None']:
        options['platform'] = openmm.Platform.getPlatformByName(
            args['--platform'])
    if args['--precision']:
        # We need to modify the Platform object.
        if args['--platform'] is None:
            raise Exception(
                "The --platform argument must be specified in order to specify platform precision."
            )

        # Set platform precision.
        precision = args['--precision']
        platform_name = args['--platform']
        logger.info(
            "Setting %s platform to use precision model '%s'." % platform_name,
            precision)
        if precision is not None:
            if platform_name == 'CUDA':
                options['platform'].setPropertyDefaultValue(
                    'CudaPrecision', precision)
            elif platform_name == 'OpenCL':
                options['platform'].setPropertyDefaultValue(
                    'OpenCLPrecision', precision)
            elif platform_name == 'CPU':
                if precision != 'mixed':
                    raise Exception(
                        "CPU platform does not support precision model '%s'; only 'mixed' is supported."
                        % precision)
            elif platform_name == 'Reference':
                if precision != 'double':
                    raise Exception(
                        "Reference platform does not support precision model '%s'; only 'double' is supported."
                        % precision)
            else:
                raise Exception(
                    "Platform selection logic is outdated and needs to be updated to add platform '%s'."
                    % platform_name)

    # Create new simulation.
    yank.create(phases,
                systems,
                positions,
                atom_indices,
                thermodynamic_state,
                options=options)

    # Report success.
    return True
예제 #7
0
파일: prepare.py 프로젝트: sonyahanson/yank
def dispatch_binding(args):
    """
    Set up a binding free energy calculation.

    Parameters
    ----------
    args : dict
       Command-line arguments from docopt.

    """

    verbose = args["--verbose"]
    store_dir = args["--store"]
    utils.config_root_logger(verbose, log_file_path=os.path.join(store_dir, "prepare.log"))

    #
    # Determine simulation options.
    #

    # Specify thermodynamic parameters.
    temperature = process_unit_bearing_arg(args, "--temperature", unit.kelvin)
    pressure = process_unit_bearing_arg(args, "--pressure", unit.atmospheres)
    thermodynamic_state = ThermodynamicState(temperature=temperature, pressure=pressure)

    # Create systems according to specified setup/import method.
    if args["amber"]:
        [phases, systems, positions, atom_indices] = setup_binding_amber(args)
    elif args["gromacs"]:
        [phases, systems, positions, atom_indices] = setup_binding_gromacs(args)
    else:
        logger.error(
            "No valid binding free energy calculation setup command specified: Must be one of ['amber', 'systembuilder']."
        )
        # Trigger help argument to be returned.
        return False

    # Report some useful properties.
    if verbose:
        if "complex-explicit" in atom_indices:
            phase = "complex-explicit"
        else:
            phase = "complex-implicit"
        logger.info("TOTAL ATOMS      : %9d" % len(atom_indices[phase]["complex"]))
        logger.info("receptor         : %9d" % len(atom_indices[phase]["receptor"]))
        logger.info("ligand           : %9d" % len(atom_indices[phase]["ligand"]))
        if phase == "complex-explicit":
            logger.info("solvent and ions : %9d" % len(atom_indices[phase]["solvent"]))

    # Initialize YANK object.
    yank = Yank(store_dir)

    # Set options.
    options = dict()
    if args["--nsteps"]:
        options["nsteps_per_iteration"] = int(args["--nsteps"])
    if args["--iterations"]:
        options["number_of_iterations"] = int(args["--iterations"])
    if args["--equilibrate"]:
        options["number_of_equilibration_iterations"] = int(args["--equilibrate"])
    if args["--online-analysis"]:
        options["online_analysis"] = True
    if args["--restraints"]:
        options["restraint_type"] = args["--restraints"]
    if args["--randomize-ligand"]:
        options["randomize_ligand"] = True
    if args["--minimize"]:
        options["minimize"] = True

    # Allow platform to be optionally specified in order for alchemical tests to be carried out.
    if args["--platform"] not in [None, "None"]:
        options["platform"] = openmm.Platform.getPlatformByName(args["--platform"])
    if args["--precision"]:
        # We need to modify the Platform object.
        if args["--platform"] is None:
            raise Exception("The --platform argument must be specified in order to specify platform precision.")

        # Set platform precision.
        precision = args["--precision"]
        platform_name = args["--platform"]
        logger.info("Setting %s platform to use precision model '%s'." % platform_name, precision)
        if precision is not None:
            if platform_name == "CUDA":
                options["platform"].setPropertyDefaultValue("CudaPrecision", precision)
            elif platform_name == "OpenCL":
                options["platform"].setPropertyDefaultValue("OpenCLPrecision", precision)
            elif platform_name == "CPU":
                if precision != "mixed":
                    raise Exception(
                        "CPU platform does not support precision model '%s'; only 'mixed' is supported." % precision
                    )
            elif platform_name == "Reference":
                if precision != "double":
                    raise Exception(
                        "Reference platform does not support precision model '%s'; only 'double' is supported."
                        % precision
                    )
            else:
                raise Exception(
                    "Platform selection logic is outdated and needs to be updated to add platform '%s'." % platform_name
                )

    yank.options.cli = options

    # Parse YAML configuration file and create YankOptions object
    if args["--yaml"]:
        yank.options.yaml = YamlBuilder(args["--yaml"]).options

    # Create new simulation.
    yank.create(phases, systems, positions, atom_indices, thermodynamic_state)

    # Report success.
    return True
예제 #8
0
#     output += "Analytical binding free energy                                  : %10.5f +- %10.5f kT\n" % (binding_free_energy / kT, 0)
#     output += "Computed binding free energy (with standard state correction)   : %10.5f +- %10.5f kT (nsigma = %3.1f)\n" % (Delta_f, dDelta_f, nsigma)
#     output += "Computed binding free energy (without standard state correction): %10.5f +- %10.5f kT (nsigma = %3.1f)\n" % (Delta_f + standard_state_correction, dDelta_f, nsigma)
#     output += "Standard state correction alone                                 : %10.5f           kT\n" % (standard_state_correction)
#     print(output)
#
#     #if (nsigma > NSIGMA_MAX):
#     #    output += "\n"
#     #    output += "Computed binding free energy differs from true binding free energy.\n"
#     #    raise Exception(output)
#
#     return [Delta_f, dDelta_f]

if __name__ == '__main__':
    from yank import utils
    utils.config_root_logger(True, log_file_path='test_LennardJones_pair.log')

    box_width_nsigma_values = np.array(
        [3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0])
    Delta_f_n = list()
    dDelta_f_n = list()
    for (n, box_width_nsigma) in enumerate(box_width_nsigma_values):
        [Delta_f,
         dDelta_f] = notest_LennardJonesPair(box_width_nsigma=box_width_nsigma)
        Delta_f_n.append(Delta_f)
        dDelta_f_n.append(dDelta_f)
    Delta_f_n = np.array(Delta_f_n)
    dDelta_f_n = np.array(dDelta_f_n)

    for (box_width_nsigma, Delta_f, dDelta_f) in zip(box_width_nsigma_values,
                                                     Delta_f_n, dDelta_f_n):
예제 #9
0
def test_replica_exchange(mpicomm=None, verbose=True):
    """
    Test that free energies and average potential energies of a 3D harmonic oscillator are correctly computed by parallel tempering.

    TODO

    * Test ParallelTempering and HamiltonianExchange subclasses as well.
    * Test with different combinations of input parameters.

    """

    if verbose and ((not mpicomm) or (mpicomm.rank==0)): sys.stdout.write("Testing replica exchange facility with harmonic oscillators: ")

    # Define mass of carbon atom.
    mass = 12.0 * units.amu

    # Define thermodynamic states.
    states = list() # thermodynamic states
    Ks = [500.00, 400.0, 300.0] * units.kilocalories_per_mole / units.angstroms**2 # spring constants
    temperatures = [300.0, 350.0, 400.0] * units.kelvin # temperatures
    seed_positions = list()
    analytical_results = list()
    f_i_analytical = list() # dimensionless free energies
    u_i_analytical = list() # reduced potential
    for (K, temperature) in zip(Ks, temperatures):
        # Create harmonic oscillator system.
        testsystem = testsystems.HarmonicOscillator(K=K, mass=mass, mm=openmm)
        [system, positions] = [testsystem.system, testsystem.positions]
        # Create thermodynamic state.
        state = ThermodynamicState(system=system, temperature=temperature)
        # Append thermodynamic state and positions.
        states.append(state)
        seed_positions.append(positions)
        # Store analytical results.
        results = computeHarmonicOscillatorExpectations(K, mass, temperature)
        analytical_results.append(results)
        f_i_analytical.append(results['f'])
        kT = kB * temperature # thermal energy
        reduced_potential = results['potential']['mean'] / kT
        u_i_analytical.append(reduced_potential)

    # Compute analytical Delta_f_ij
    nstates = len(f_i_analytical)
    f_i_analytical = numpy.array(f_i_analytical)
    u_i_analytical = numpy.array(u_i_analytical)
    s_i_analytical = u_i_analytical - f_i_analytical
    Delta_f_ij_analytical = numpy.zeros([nstates,nstates], numpy.float64)
    Delta_u_ij_analytical = numpy.zeros([nstates,nstates], numpy.float64)
    Delta_s_ij_analytical = numpy.zeros([nstates,nstates], numpy.float64)
    for i in range(nstates):
        for j in range(nstates):
            Delta_f_ij_analytical[i,j] = f_i_analytical[j] - f_i_analytical[i]
            Delta_u_ij_analytical[i,j] = u_i_analytical[j] - u_i_analytical[i]
            Delta_s_ij_analytical[i,j] = s_i_analytical[j] - s_i_analytical[i]

    # Define file for temporary storage.
    import tempfile # use a temporary file
    file = tempfile.NamedTemporaryFile(delete=False)
    store_filename = file.name
    #print("node %d : Storing data in temporary file: %s" % (mpicomm.rank, str(store_filename))) # DEBUG

    # Create and configure simulation object.
    simulation = ReplicaExchange(store_filename, mpicomm=mpicomm)
    simulation.create(states, seed_positions)
    simulation.platform = openmm.Platform.getPlatformByName('Reference')
    simulation.minimize = False
    simulation.number_of_iterations = 200
    simulation.nsteps_per_iteration = 500
    simulation.timestep = 2.0 * units.femtoseconds
    simulation.collision_rate = 20.0 / units.picosecond
    simulation.verbose = False
    simulation.show_mixing_statistics = False
    simulation.online_analysis = True

    # Run simulation.
    utils.config_root_logger(False)
    simulation.run() # run the simulation
    utils.config_root_logger(True)

    # Stop here if not root node.
    if mpicomm and (mpicomm.rank != 0): return

    # Retrieve extant analysis object.
    online_analysis = simulation.analysis

    # Analyze simulation to compute free energies.
    analysis = simulation.analyze()

    # Check if online analysis is close to final analysis.
    error = numpy.abs(online_analysis['Delta_f_ij'] - analysis['Delta_f_ij'])
    derror = (online_analysis['dDelta_f_ij']**2 + analysis['dDelta_f_ij']**2)
    indices = numpy.where(derror > 0.0)
    nsigma = numpy.zeros([nstates,nstates], numpy.float32)
    nsigma[indices] = error[indices] / derror[indices]
    MAX_SIGMA = 6.0 # maximum allowed number of standard errors
    if numpy.any(nsigma > MAX_SIGMA):
        print("Delta_f_ij from online analysis")
        print(online_analysis['Delta_f_ij'])
        print("Delta_f_ij from final analysis")
        print(analysis['Delta_f_ij'])
        print("error")
        print(error)
        print("derror")
        print(derror)
        print("nsigma")
        print(nsigma)
        raise Exception("Dimensionless free energy differences between online and final analysis exceeds MAX_SIGMA of %.1f" % MAX_SIGMA)

    # TODO: Check if deviations exceed tolerance.
    Delta_f_ij = analysis['Delta_f_ij']
    dDelta_f_ij = analysis['dDelta_f_ij']
    error = numpy.abs(Delta_f_ij - Delta_f_ij_analytical)
    indices = numpy.where(dDelta_f_ij > 0.0)
    nsigma = numpy.zeros([nstates,nstates], numpy.float32)
    nsigma[indices] = error[indices] / dDelta_f_ij[indices]
    MAX_SIGMA = 6.0 # maximum allowed number of standard errors
    if numpy.any(nsigma > MAX_SIGMA):
        print("Delta_f_ij")
        print(Delta_f_ij)
        print("Delta_f_ij_analytical")
        print(Delta_f_ij_analytical)
        print("error")
        print(error)
        print("stderr")
        print(dDelta_f_ij)
        print("nsigma")
        print(nsigma)
        raise Exception("Dimensionless free energy difference exceeds MAX_SIGMA of %.1f" % MAX_SIGMA)

    error = analysis['Delta_u_ij'] - Delta_u_ij_analytical
    nsigma = numpy.zeros([nstates,nstates], numpy.float32)
    nsigma[indices] = error[indices] / dDelta_f_ij[indices]
    if numpy.any(nsigma > MAX_SIGMA):
        print("Delta_u_ij")
        print(analysis['Delta_u_ij'])
        print("Delta_u_ij_analytical")
        print(Delta_u_ij_analytical)
        print("error")
        print(error)
        print("nsigma")
        print(nsigma)
        raise Exception("Dimensionless potential energy difference exceeds MAX_SIGMA of %.1f" % MAX_SIGMA)

    # Clean up.
    del simulation

    if verbose: print("PASSED.")
    return
예제 #10
0
def dispatch(args):
    from yank import analyze
    utils.config_root_logger(args['--verbose'])
    success = analyze.print_status(args['--store'])
    return success
예제 #11
0
def dispatch(args):
    from yank import analyze
    utils.config_root_logger(args['--verbose'])
    analyze.analyze(args['--store'])
    return True
예제 #12
0
if not os.path.exists(workdir):
    os.makedirs(workdir)
    logger.info("Creating path %s" % workdir)

# Create directory to store files in.
outdir = os.path.join(os.getcwd(), store_dir)
if not os.path.exists(outdir):
    os.makedirs(outdir)
    logger.info("Creating path %s" % outdir)

# ==============================================================================
# CONFIGURE LOGGER
# ==============================================================================

from yank import utils
utils.config_root_logger(verbose, log_file_path=os.path.join(setup_dir, 'prepare.log'))

# ==============================================================================
# PREPARE STRUCTURE
# ==============================================================================

from pdbfixer import PDBFixer

is_periodic = (nonbonded_method not in [app.NoCutoff, app.CutoffNonPeriodic])

# ==============================================================================
# Retrieve the PDB file
# ==============================================================================

if pdb_filename:
    logger.info("Retrieving PDB '%s'..." % pdb_filename)
예제 #13
0
def dispatch_binding(args):
    """
    Set up a binding free energy calculation.

    Parameters
    ----------
    args : dict
       Command-line arguments from docopt.

    """

    verbose = args['--verbose']
    store_dir = args['--store']
    utils.config_root_logger(verbose, log_file_path=os.path.join(store_dir, 'prepare.log'))

    #
    # Determine simulation options.
    #

    # Specify thermodynamic parameters.
    temperature = process_unit_bearing_arg(args, '--temperature', unit.kelvin)
    pressure = process_unit_bearing_arg(args, '--pressure', unit.atmospheres)
    thermodynamic_state = ThermodynamicState(temperature=temperature, pressure=pressure)

    # Create systems according to specified setup/import method.
    if args['amber']:
        [phases, systems, positions, atom_indices] = setup_binding_amber(args)
    elif args['gromacs']:
        [phases, systems, positions, atom_indices] = setup_binding_gromacs(args)
    else:
        logger.error("No valid binding free energy calculation setup command specified: Must be one of ['amber', 'systembuilder'].")
        # Trigger help argument to be returned.
        return False

    # Report some useful properties.
    if verbose:
        if 'complex-explicit' in atom_indices:
            phase = 'complex-explicit'
        else:
            phase = 'complex-implicit'
        logger.info("TOTAL ATOMS      : %9d" % len(atom_indices[phase]['complex']))
        logger.info("receptor         : %9d" % len(atom_indices[phase]['receptor']))
        logger.info("ligand           : %9d" % len(atom_indices[phase]['ligand']))
        if phase == 'complex-explicit':
            logger.info("solvent and ions : %9d" % len(atom_indices[phase]['solvent']))

    # Set options.
    options = dict()
    if args['--nsteps']:
        options['nsteps_per_iteration'] = int(args['--nsteps'])
    if args['--iterations']:
        options['number_of_iterations'] = int(args['--iterations'])
    if args['--equilibrate']:
        options['number_of_equilibration_iterations'] = int(args['--equilibrate'])
    if args['--online-analysis']:
        options['online_analysis'] = True
    if args['--restraints']:
        options['restraint_type'] = args['--restraints']
    if args['--randomize-ligand']:
        options['randomize_ligand'] = True
    if args['--minimize']:
        options['minimize'] = True

    # Allow platform to be optionally specified in order for alchemical tests to be carried out.
    if args['--platform'] not in [None, 'None']:
        options['platform'] = openmm.Platform.getPlatformByName(args['--platform'])
    if args['--precision']:
        # We need to modify the Platform object.
        if args['--platform'] is None:
            raise Exception("The --platform argument must be specified in order to specify platform precision.")

        # Set platform precision.
        precision = args['--precision']
        platform_name = args['--platform']
        logger.info("Setting %s platform to use precision model '%s'." % platform_name, precision)
        if precision is not None:
            if platform_name == 'CUDA':
                options['platform'].setPropertyDefaultValue('CudaPrecision', precision)
            elif platform_name == 'OpenCL':
                options['platform'].setPropertyDefaultValue('OpenCLPrecision', precision)
            elif platform_name == 'CPU':
                if precision != 'mixed':
                    raise Exception("CPU platform does not support precision model '%s'; only 'mixed' is supported." % precision)
            elif platform_name == 'Reference':
                if precision != 'double':
                    raise Exception("Reference platform does not support precision model '%s'; only 'double' is supported." % precision)
            else:
                raise Exception("Platform selection logic is outdated and needs to be updated to add platform '%s'." % platform_name)

    # Parse YAML options, CLI options have priority
    if args['--yaml']:
        options.update(YamlBuilder(args['--yaml']).yank_options)

    # Create new simulation.
    yank = Yank(store_dir, **options)
    yank.create(phases, systems, positions, atom_indices, thermodynamic_state)

    # Report success.
    return True
예제 #14
0
def dispatch(args):
    from yank import analyze
    utils.config_root_logger(args['--verbose'])
    success = analyze.print_status(args['--store'])
    return success
예제 #15
0
def dispatch_binding(args):
    """
    Set up a binding free energy calculation.

    Parameters
    ----------
    args : dict
       Command-line arguments from docopt.

    """

    verbose = args['--verbose']
    store_dir = args['--store']
    utils.config_root_logger(verbose,
                             log_file_path=os.path.join(
                                 store_dir, 'prepare.log'))

    #
    # Determine simulation options.
    #

    # Specify thermodynamic parameters.
    temperature = process_unit_bearing_arg(args, '--temperature', unit.kelvin)
    pressure = process_unit_bearing_arg(args, '--pressure', unit.atmospheres)
    thermodynamic_state = ThermodynamicState(temperature=temperature,
                                             pressure=pressure)

    # Create systems according to specified setup/import method.
    if args['amber']:
        alchemical_phases = setup_binding_amber(args)
    elif args['gromacs']:
        alchemical_phases = setup_binding_gromacs(args)
    else:
        logger.error(
            "No valid binding free energy calculation setup command specified: Must be one of ['amber', 'systembuilder']."
        )
        # Trigger help argument to be returned.
        return False

    # Set options.
    options = dict()
    if args['--nsteps']:
        options['nsteps_per_iteration'] = int(args['--nsteps'])
    if args['--iterations']:
        options['number_of_iterations'] = int(args['--iterations'])
    if args['--equilibrate']:
        options['number_of_equilibration_iterations'] = int(
            args['--equilibrate'])
    if args['--online-analysis']:
        options['online_analysis'] = True
    if args['--restraints']:
        options['restraint_type'] = args['--restraints']
    if args['--randomize-ligand']:
        options['randomize_ligand'] = True
    if args['--minimize']:
        options['minimize'] = True

    # Allow platform to be optionally specified in order for alchemical tests to be carried out.
    if args['--platform'] not in [None, 'None']:
        options['platform'] = openmm.Platform.getPlatformByName(
            args['--platform'])
    if args['--precision']:
        # We need to modify the Platform object.
        if args['--platform'] is None:
            raise Exception(
                "The --platform argument must be specified in order to specify platform precision."
            )

        # Set platform precision.
        precision = args['--precision']
        platform_name = args['--platform']
        logger.info(
            "Setting %s platform to use precision model '%s'." % platform_name,
            precision)
        if precision is not None:
            if platform_name == 'CUDA':
                options['platform'].setPropertyDefaultValue(
                    'CudaPrecision', precision)
            elif platform_name == 'OpenCL':
                options['platform'].setPropertyDefaultValue(
                    'OpenCLPrecision', precision)
            elif platform_name == 'CPU':
                if precision != 'mixed':
                    raise Exception(
                        "CPU platform does not support precision model '%s'; only 'mixed' is supported."
                        % precision)
            elif platform_name == 'Reference':
                if precision != 'double':
                    raise Exception(
                        "Reference platform does not support precision model '%s'; only 'double' is supported."
                        % precision)
            else:
                raise Exception(
                    "Platform selection logic is outdated and needs to be updated to add platform '%s'."
                    % platform_name)

    # Parse YAML options, CLI options have priority
    if args['--yaml']:
        options.update(YamlBuilder(args['--yaml']).yank_options)

    # Create new simulation.
    yank = Yank(store_dir, **options)
    yank.create(thermodynamic_state, *alchemical_phases)

    # Dump analysis object
    analysis = [[alchemical_phases[0].name, 1],
                [alchemical_phases[1].name, -1]]
    analysis_script_path = os.path.join(store_dir, 'analysis.yaml')
    with open(analysis_script_path, 'w') as f:
        yaml.dump(analysis, f)

    # Report success.
    return True
예제 #16
0
def run_replica_exchange(topology,system,positions,temperature_list=None,simulation_time_step=None,total_simulation_time=1.0 * unit.picosecond,output_data='output.nc',print_frequency=100,verbose_simulation=False,exchange_attempts=None,test_time_step=False,output_directory=None):
        """
        Run a Yank replica exchange simulation using an OpenMM coarse grained model.

        :param topology: OpenMM Topology
        :type topology: `Topology() <https://simtk.org/api_docs/openmm/api4_1/python/classsimtk_1_1openmm_1_1app_1_1topology_1_1Topology.html>`_

        :param system: OpenMM System()
        :type system: `System() <https://simtk.org/api_docs/openmm/api4_1/python/classsimtk_1_1openmm_1_1openmm_1_1System.html>`_

        :param positions: Positions array for the model we would like to test
        :type positions: `Quantity() <http://docs.openmm.org/development/api-python/generated/simtk.unit.quantity.Quantity.html>`_ ( np.array( [cgmodel.num_beads,3] ), simtk.unit )

        :param temperature_list: List of temperatures for which to perform replica exchange simulations, default = None
        :type temperature: List( float * simtk.unit.temperature )

        :param simulation_time_step: Simulation integration time step
        :type simulation_time_step: `SIMTK <https://simtk.org/>`_ `Unit() <http://docs.openmm.org/7.1.0/api-python/generated/simtk.unit.unit.Unit.html>`_

        :param total_simulation_time: Total run time for individual simulations
        :type total_simulation_time: `SIMTK <https://simtk.org/>`_ `Unit() <http://docs.openmm.org/7.1.0/api-python/generated/simtk.unit.unit.Unit.html>`_

        :param output_data: Name of NETCDF file where we will write simulation data
        :type output_data: string

        :param print_frequency: Number of simulation steps to skip when writing to output, Default = 100
        :type print_frequence: int

        :param verbose_simulation: Determines how much output is printed during a simulation run.  Default = False
        :type verbose_simulation: Logical

        :param exchange_attempts: Number of exchange attempts to make during a replica exchange simulation run, Default = None
        :type exchange_attempts: int

        :param test_time_step: Logical variable determining if a test of the time step will be performed, Default = False
        :type test_time_step: Logical

        :param output_directory: Path to which we will write the output from simulation runs.
        :type output_directory: str

        :returns: 
            - replica_energies ( `Quantity() <http://docs.openmm.org/development/api-python/generated/simtk.unit.quantity.Quantity.html>`_ ( np.float( [number_replicas,number_simulation_steps] ), simtk.unit ) ) - The potential energies for all replicas at all (printed) time steps
            - replica_positions ( `Quantity() <http://docs.openmm.org/development/api-python/generated/simtk.unit.quantity.Quantity.html>`_ ( np.float( [number_replicas,number_simulation_steps,cgmodel.num_beads,3] ), simtk.unit ) ) - The positions for all replicas at all (printed) time steps
 
            - replica_state_indices ( np.int64( [number_replicas,number_simulation_steps] ), simtk.unit ) - The thermodynamic state assignments for all replicas at all (printed) time steps

        :Example:

        >>> from foldamers.cg_model.cgmodel import CGModel
        >>> from cg_openmm.simulation.rep_exch import *
        >>> cgmodel = CGModel()
        >>> replica_energies,replica_positions,replica_state_indices = run_replica_exchange(cgmodel.topology,cgmodel.system,cgmodel.positions)

        """
        if simulation_time_step == None:
          simulation_time_step,force_threshold = get_simulation_time_step(topology,system,positions,temperature_list[-1],total_simulation_time)

        simulation_steps = int(round(total_simulation_time.__div__(simulation_time_step)))

        if exchange_attempts == None:
          if simulation_steps > 10000:
            exchange_attempts = round(simulation_steps/1000)
          else:
            exchange_attempts = 10

        if temperature_list == None:
          temperature_list = [(300.0 * unit.kelvin).__add__(i * unit.kelvin) for i in range(-50,50,10)]

        num_replicas = len(temperature_list)
        sampler_states = list()
        thermodynamic_states = list()

        # Define thermodynamic states.
        box_vectors = system.getDefaultPeriodicBoxVectors()
        for temperature in temperature_list:
          thermodynamic_state = mmtools.states.ThermodynamicState(system=system, temperature=temperature)
          thermodynamic_states.append(thermodynamic_state)
          sampler_states.append(mmtools.states.SamplerState(positions,box_vectors=box_vectors))

        # Create and configure simulation object.
        move = mmtools.mcmc.LangevinDynamicsMove(timestep=simulation_time_step,collision_rate=5.0/unit.picosecond,n_steps=exchange_attempts, reassign_velocities=True)
        simulation = ReplicaExchangeSampler(mcmc_moves=move, number_of_iterations=exchange_attempts)

        if os.path.exists(output_data): os.remove(output_data)
        reporter = MultiStateReporter(output_data, checkpoint_interval=1)
        simulation.create(thermodynamic_states, sampler_states, reporter)
        config_root_logger(verbose_simulation)
       
        if not test_time_step:
           num_attempts = 0
           while num_attempts < 5:
            try:
              simulation.run()
              #print("Replica exchange simulations succeeded with a time step of: "+str(simulation_time_step))
              break
            except:
              num_attempts = num_attempts + 1
           if num_attempts >= 5:
             print("Replica exchange simulation attempts failed, try verifying your model/simulation settings.")
             exit()
        else:
          simulation_time_step,force_threshold = get_simulation_time_step(topology,system,positions,temperature_list[-1],total_simulation_time)
          print("The suggested time step for a simulation with this model is: "+str(simulation_time_step))
          while simulation_time_step.__div__(2.0) > 0.001 * unit.femtosecond:
          
            try:
              print("Running replica exchange simulations with Yank...")
              print("Using a time step of "+str(simulation_time_step))
              print("Running each trial simulation for 1000 steps, with 10 exchange attempts.")
              move = mmtools.mcmc.LangevinDynamicsMove(timestep=simulation_time_step,collision_rate=20.0/unit.picosecond,n_steps=10, reassign_velocities=True)
              simulation = ReplicaExchangeSampler(replica_mixing_scheme='swap-neighbors',mcmc_moves=move,number_of_iterations=10)
              reporter = MultiStateReporter(output_data, checkpoint_interval=1)
              simulation.create(thermodynamic_states, sampler_states, reporter)

              simulation.run()
              print("Replica exchange simulations succeeded with a time step of: "+str(simulation_time_step))
              break
            except:
              del simulation
              os.remove(output_data)
              print("Simulation attempt failed with a time step of: "+str(simulation_time_step))
              if simulation_time_step.__div__(2.0) > 0.001 * unit.femtosecond:
                simulation_time_step = simulation_time_step.__div__(2.0)
              else:
                print("Error: replica exchange simulation attempt failed with a time step of: "+str(simulation_time_step))
                print("Please check the model and simulations settings, and try again.")
                exit()

        replica_energies,replica_positions,replica_state_indices = read_replica_exchange_data(system=system,topology=topology,temperature_list=temperature_list,output_data=output_data,print_frequency=print_frequency)

        steps_per_stage = round(simulation_steps/exchange_attempts)
        if output_directory != None:
          plot_replica_exchange_energies(replica_energies,temperature_list,simulation_time_step,steps_per_stage=steps_per_stage,output_directory=output_directory)
          plot_replica_exchange_summary(replica_state_indices,temperature_list,simulation_time_step,steps_per_stage=steps_per_stage,output_directory=output_directory)
        else:
          plot_replica_exchange_energies(replica_energies,temperature_list,simulation_time_step,steps_per_stage=steps_per_stage)
          plot_replica_exchange_summary(replica_state_indices,temperature_list,simulation_time_step,steps_per_stage=steps_per_stage)

        return(replica_energies,replica_positions,replica_state_indices)
예제 #17
0
    repex = ReplicaExchange(store_filename='test', nsteps_per_iteration=1e6)
    assert repex.nsteps_per_iteration == 1000000
    assert repex.collision_rate == repex.default_parameters['collision_rate']

@tools.raises(TypeError)
def test_unknown_parameters():
    """Test ReplicaExchange raises exception on wrong initialization."""
    ReplicaExchange(store_filename='test', wrong_parameter=False)

#=============================================================================================
# MAIN AND TESTS
#=============================================================================================

if __name__ == "__main__":
    # Configure logger.
    utils.config_root_logger(False)

    # Try MPI, if possible.
    try:
        mpicomm = utils.initialize_mpi()
        if mpicomm.rank == 0:
            print("MPI initialized successfully.")
    except Exception as e:
        print(e)
        print("Could not start MPI. Using serial code instead.")
        mpicomm = None

    # Test simple system of harmonic oscillators.
    # Disabled until we fix the test
    # test_hamiltonian_exchange(mpicomm)
    test_replica_exchange(mpicomm)
예제 #18
0
        print error
        print "nsigma"
        print nsigma
        raise Exception("Dimensionless potential energy difference exceeds MAX_SIGMA of %.1f" % MAX_SIGMA)

    if verbose: print "PASSED."
    return

#=============================================================================================
# MAIN AND TESTS
#=============================================================================================

if __name__ == "__main__":
    # Configure logger.
    from yank import utils
    utils.config_root_logger(True, log_file_path='debug.log')

    # Try MPI, if possible.
    try:
        from mpi4py import MPI # MPI wrapper
        hostname = os.uname()[1]
        mpicomm = MPI.COMM_WORLD
        if mpicomm.rank == 0:
            print "MPI initialized successfully."
    except Exception as e:
        print e
        print "Could not start MPI. Using serial code instead."
        mpicomm = None

    # Test simple system of harmonic oscillators.
    test_hamiltonian_exchange(mpicomm)
예제 #19
0
def notest_hamiltonian_exchange(mpicomm=None, verbose=True):
    """
    Test that free energies and average potential energies of a 3D harmonic oscillator are correctly computed when running HamiltonianExchange.

    TODO

    * Integrate with test_replica_exchange.
    * Test with different combinations of input parameters.

    """

    if verbose and ((not mpicomm) or (mpicomm.rank==0)): sys.stdout.write("Testing Hamiltonian exchange facility with harmonic oscillators: ")

    # Create test system of harmonic oscillators
    testsystem = testsystems.HarmonicOscillatorArray()
    [system, coordinates] = [testsystem.system, testsystem.positions]

    # Define mass of carbon atom.
    mass = 12.0 * units.amu

    # Define thermodynamic states.
    sigmas = [0.2, 0.3, 0.4] * units.angstroms # standard deviations: beta K = 1/sigma^2 so K = 1/(beta sigma^2)
    temperature = 300.0 * units.kelvin # temperatures
    seed_positions = list()
    analytical_results = list()
    f_i_analytical = list() # dimensionless free energies
    u_i_analytical = list() # reduced potential
    systems = list() # Systems list for HamiltonianExchange
    for sigma in sigmas:
        # Compute corresponding spring constant.
        kB = units.BOLTZMANN_CONSTANT_kB * units.AVOGADRO_CONSTANT_NA
        kT = kB * temperature # thermal energy
        beta = 1.0 / kT # inverse temperature
        K = 1.0 / (beta * sigma**2)
        # Create harmonic oscillator system.
        testsystem = testsystems.HarmonicOscillator(K=K, mass=mass, mm=openmm)
        [system, positions] = [testsystem.system, testsystem.positions]
        # Append to systems list.
        systems.append(system)
        # Append positions.
        seed_positions.append(positions)
        # Store analytical results.
        results = computeHarmonicOscillatorExpectations(K, mass, temperature)
        analytical_results.append(results)
        f_i_analytical.append(results['f'])
        reduced_potential = results['potential']['mean'] / kT
        u_i_analytical.append(reduced_potential)

    # DEBUG
    print("")
    print(seed_positions)
    print(analytical_results)
    print(u_i_analytical)
    print(f_i_analytical)
    print("")

    # Compute analytical Delta_f_ij
    nstates = len(f_i_analytical)
    f_i_analytical = numpy.array(f_i_analytical)
    u_i_analytical = numpy.array(u_i_analytical)
    s_i_analytical = u_i_analytical - f_i_analytical
    Delta_f_ij_analytical = numpy.zeros([nstates,nstates], numpy.float64)
    Delta_u_ij_analytical = numpy.zeros([nstates,nstates], numpy.float64)
    Delta_s_ij_analytical = numpy.zeros([nstates,nstates], numpy.float64)
    for i in range(nstates):
        for j in range(nstates):
            Delta_f_ij_analytical[i,j] = f_i_analytical[j] - f_i_analytical[i]
            Delta_u_ij_analytical[i,j] = u_i_analytical[j] - u_i_analytical[i]
            Delta_s_ij_analytical[i,j] = s_i_analytical[j] - s_i_analytical[i]

    # Define file for temporary storage.
    import tempfile # use a temporary file
    file = tempfile.NamedTemporaryFile(delete=False)
    store_filename = file.name
    #print("Storing data in temporary file: %s" % str(store_filename))

    # Create reference thermodynamic state.
    reference_state = ThermodynamicState(systems[0], temperature=temperature)

    # Create and configure simulation object.
    simulation = HamiltonianExchange(store_filename, mpicomm=mpicomm)
    simulation.create(reference_state, systems, seed_positions)
    simulation.platform = openmm.Platform.getPlatformByName('Reference')
    simulation.number_of_iterations = 200
    simulation.timestep = 2.0 * units.femtoseconds
    simulation.nsteps_per_iteration = 500
    simulation.collision_rate = 9.2 / units.picosecond
    simulation.verbose = False
    simulation.show_mixing_statistics = False

    # Run simulation.
    utils.config_root_logger(True)
    simulation.run() # run the simulation
    utils.config_root_logger(False)

    # Stop here if not root node.
    if mpicomm and (mpicomm.rank != 0): return

    # Analyze simulation to compute free energies.
    analysis = simulation.analyze()

    # TODO: Check if deviations exceed tolerance.
    Delta_f_ij = analysis['Delta_f_ij']
    dDelta_f_ij = analysis['dDelta_f_ij']
    error = Delta_f_ij - Delta_f_ij_analytical
    indices = numpy.where(dDelta_f_ij > 0.0)
    nsigma = numpy.zeros([nstates,nstates], numpy.float32)
    nsigma[indices] = error[indices] / dDelta_f_ij[indices]
    MAX_SIGMA = 6.0 # maximum allowed number of standard errors
    if numpy.any(nsigma > MAX_SIGMA):
        print("Delta_f_ij")
        print(Delta_f_ij)
        print("Delta_f_ij_analytical")
        print(Delta_f_ij_analytical)
        print("error")
        print(error)
        print("stderr")
        print(dDelta_f_ij)
        print("nsigma")
        print(nsigma)
        raise Exception("Dimensionless free energy difference exceeds MAX_SIGMA of %.1f" % MAX_SIGMA)

    error = analysis['Delta_u_ij'] - Delta_u_ij_analytical
    nsigma = numpy.zeros([nstates,nstates], numpy.float32)
    nsigma[indices] = error[indices] / dDelta_f_ij[indices]
    if numpy.any(nsigma > MAX_SIGMA):
        print("Delta_u_ij")
        print(analysis['Delta_u_ij'])
        print("Delta_u_ij_analytical")
        print(Delta_u_ij_analytical)
        print("error")
        print(error)
        print("nsigma")
        print(nsigma)
        raise Exception("Dimensionless potential energy difference exceeds MAX_SIGMA of %.1f" % MAX_SIGMA)

    if verbose: print("PASSED.")
    return
예제 #20
0
def dispatch(args):
    from yank import analyze
    utils.config_root_logger(args['--verbose'])
    analyze.analyze(args['--store'])
    return True
예제 #21
0
def dispatch(args):
    from yank.yank import Yank # TODO: Fix this awkward import syntax.

    store_directory = args['--store']

    # Set override options.
    options = dict()

    # Configure MPI, if requested.
    mpicomm = None
    if args['--mpi']:
        # Initialize MPI.
        from mpi4py import MPI
        hostname = os.uname()[1]
        MPI.COMM_WORLD.barrier()
        logger.info("Initialized MPI on %d processes." % (MPI.COMM_WORLD.size))
        mpicomm = MPI.COMM_WORLD

    # Configure logger
    utils.config_root_logger(args['--verbose'], mpicomm=mpicomm,
                             log_file_path=os.path.join(store_directory, 'run.log'))

    if args['--iterations']:
        options['number_of_iterations'] = int(args['--iterations'])
    if args['--online-analysis']:
        options['online_analysis'] = True
    if args['--platform'] not in [None, 'None']:
        options['platform'] = openmm.Platform.getPlatformByName(args['--platform'])
    if args['--precision']:
        # We need to modify the Platform object.
        if args['--platform'] is None:
            raise Exception("The --platform argument must be specified in order to specify platform precision.")

        # Set platform precision.
        precision = args['--precision']
        platform_name = args['--platform']
        logger.info("Setting %s platform to use precision model '%s'." % (platform_name, precision))
        if precision is not None:
            if platform_name == 'CUDA':
                options['platform'].setPropertyDefaultValue('CudaPrecision', precision)
            elif platform_name == 'OpenCL':
                options['platform'].setPropertyDefaultValue('OpenCLPrecision', precision)
            elif platform_name == 'CPU':
                if precision != 'mixed':
                    raise Exception("CPU platform does not support precision model '%s'; only 'mixed' is supported." % precision)
            elif platform_name == 'Reference':
                if precision != 'double':
                    raise Exception("Reference platform does not support precision model '%s'; only 'double' is supported." % precision)
            else:
                raise Exception("Platform selection logic is outdated and needs to be updated to add platform '%s'." % platform_name)

    # Create YANK object associated with data storage directory.
    yank = Yank(store_directory, mpicomm=mpicomm, **options)

    # Set YANK to resume from the store file.
    phases = None # By default, resume from all phases found in store_directory
    if args['--phase']: phases=[args['--phase']]
    yank.resume(phases=phases)

    # Run simulation.
    yank.run()

    return True
예제 #22
0
파일: prepare.py 프로젝트: jchodera/yank
def dispatch_binding(args):
    """
    Set up a binding free energy calculation.

    Parameters
    ----------
    args : dict
       Command-line arguments from docopt.

    """

    verbose = args["--verbose"]
    store_dir = args["--store"]
    utils.config_root_logger(verbose, log_file_path=os.path.join(store_dir, "prepare.log"))

    #
    # Determine simulation options.
    #

    # Specify thermodynamic parameters.
    temperature = process_unit_bearing_arg(args, "--temperature", unit.kelvin)
    pressure = process_unit_bearing_arg(args, "--pressure", unit.atmospheres)
    thermodynamic_state = ThermodynamicState(temperature=temperature, pressure=pressure)

    # Create systems according to specified setup/import method.
    if args["amber"]:
        alchemical_phases = setup_binding_amber(args)
    elif args["gromacs"]:
        alchemical_phases = setup_binding_gromacs(args)
    else:
        logger.error(
            "No valid binding free energy calculation setup command specified: Must be one of ['amber', 'systembuilder']."
        )
        # Trigger help argument to be returned.
        return False

    # Set options.
    options = dict()
    if args["--nsteps"]:
        options["nsteps_per_iteration"] = int(args["--nsteps"])
    if args["--iterations"]:
        options["number_of_iterations"] = int(args["--iterations"])
    if args["--equilibrate"]:
        options["number_of_equilibration_iterations"] = int(args["--equilibrate"])
    if args["--online-analysis"]:
        options["online_analysis"] = True
    if args["--restraints"]:
        options["restraint_type"] = args["--restraints"]
    if args["--randomize-ligand"]:
        options["randomize_ligand"] = True
    if args["--minimize"]:
        options["minimize"] = True

    # Allow platform to be optionally specified in order for alchemical tests to be carried out.
    if args["--platform"] not in [None, "None"]:
        options["platform"] = openmm.Platform.getPlatformByName(args["--platform"])
    if args["--precision"]:
        # We need to modify the Platform object.
        if args["--platform"] is None:
            raise Exception("The --platform argument must be specified in order to specify platform precision.")

        # Set platform precision.
        precision = args["--precision"]
        platform_name = args["--platform"]
        logger.info("Setting %s platform to use precision model '%s'." % platform_name, precision)
        if precision is not None:
            if platform_name == "CUDA":
                options["platform"].setPropertyDefaultValue("CudaPrecision", precision)
            elif platform_name == "OpenCL":
                options["platform"].setPropertyDefaultValue("OpenCLPrecision", precision)
            elif platform_name == "CPU":
                if precision != "mixed":
                    raise Exception(
                        "CPU platform does not support precision model '%s'; only 'mixed' is supported." % precision
                    )
            elif platform_name == "Reference":
                if precision != "double":
                    raise Exception(
                        "Reference platform does not support precision model '%s'; only 'double' is supported."
                        % precision
                    )
            else:
                raise Exception(
                    "Platform selection logic is outdated and needs to be updated to add platform '%s'." % platform_name
                )

    # Parse YAML options, CLI options have priority
    if args["--yaml"]:
        options.update(YamlBuilder(args["--yaml"]).yank_options)

    # Create new simulation.
    yank = Yank(store_dir, **options)
    yank.create(thermodynamic_state, *alchemical_phases)

    # Dump analysis object
    analysis = [[alchemical_phases[0].name, 1], [alchemical_phases[1].name, -1]]
    analysis_script_path = os.path.join(store_dir, "analysis.yaml")
    with open(analysis_script_path, "w") as f:
        yaml.dump(analysis, f)

    # Report success.
    return True