Beispiel #1
0
def run_equilibrium(
        equilibrium_result: EquilibriumResult,
        thermodynamic_state: states.ThermodynamicState,
        nsteps_equil: int,
        topology: md.Topology,
        n_iterations: int,
        atom_indices_to_save: List[int] = None,
        trajectory_filename: str = None,
        splitting: str = "V R O R V",
        timestep: unit.Quantity = 1.0 * unit.femtoseconds
) -> EquilibriumResult:
    """
    Run nsteps of equilibrium sampling at the specified thermodynamic state and return the final sampler state
    as well as a trajectory of the positions after each application of an MCMove. This means that if the MCMove
    is configured to run 1000 steps of dynamics, and n_iterations is 100, there will be 100 frames in the resulting
    trajectory; these are the result of 100,000 steps (1000*100) of dynamics.

    Parameters
    ----------
    equilibrium_result : EquilibriumResult
       EquilibriumResult namedtuple containing the information necessary to resume
    thermodynamic_state : openmmtools.states.ThermodynamicState
        The thermodynamic state (including context parameters) that should be used
    nsteps_equil : int
        The number of equilibrium steps that a move should make when apply is called
    topology : mdtraj.Topology
        an MDTraj topology object used to construct the trajectory
    n_iterations : int
        The number of times to apply the move. Note that this is not the number of steps of dynamics; it is
        n_iterations*n_steps (which is set in the MCMove).
    splitting: str, default "V R O H R V"
        The splitting string for the dynamics
    atom_indices_to_save : list of int, default None
        list of indices to save (when excluding waters, for instance). If None, all indices are saved.
    trajectory_filename : str, optional, default None
        Full filepath of trajectory files. If none, trajectory files are not written.
    splitting: str, default "V R O H R V"
        The splitting string for the dynamics
    Returns
    -------
    equilibrium_result : EquilibriumResult
        Container namedtuple that has the SamplerState for resuming, an MDTraj trajectory, and the reduced potential of the
        final frame.
    """
    sampler_state = equilibrium_result.sampler_state
    #get the atom indices we need to subset the topology and positions
    if atom_indices_to_save is None:
        atom_indices = list(range(topology.n_atoms))
        subset_topology = topology
    else:
        subset_topology = topology.subset(atom_indices_to_save)
        atom_indices = atom_indices_to_save

    n_atoms = subset_topology.n_atoms

    #construct the MCMove:
    mc_move = mcmc.LangevinSplittingDynamicsMove(n_steps=nsteps_equil,
                                                 splitting=splitting)
    mc_move.n_restart_attempts = 10

    #create a numpy array for the trajectory
    trajectory_positions = np.zeros([n_iterations, n_atoms, 3])
    trajectory_box_lengths = np.zeros([n_iterations, 3])
    trajectory_box_angles = np.zeros([n_iterations, 3])

    #loop through iterations and apply MCMove, then collect positions into numpy array
    for iteration in range(n_iterations):
        mc_move.apply(thermodynamic_state, sampler_state)

        trajectory_positions[iteration, :] = sampler_state.positions[
            atom_indices, :].value_in_unit_system(unit.md_unit_system)

        #get the box lengths and angles
        a, b, c, alpha, beta, gamma = mdtrajutils.unitcell.box_vectors_to_lengths_and_angles(
            *sampler_state.box_vectors)
        trajectory_box_lengths[iteration, :] = [a, b, c]
        trajectory_box_angles[iteration, :] = [alpha, beta, gamma]

    #construct trajectory object:
    trajectory = md.Trajectory(trajectory_positions,
                               subset_topology,
                               unitcell_lengths=trajectory_box_lengths,
                               unitcell_angles=trajectory_box_angles)

    #get the reduced potential from the final frame for endpoint perturbations
    reduced_potential_final_frame = thermodynamic_state.reduced_potential(
        sampler_state)

    #construct equilibrium result object
    equilibrium_result = EquilibriumResult(sampler_state,
                                           reduced_potential_final_frame)

    #If there is a trajectory filename passed, write out the results here:
    if trajectory_filename is not None:
        write_equilibrium_trajectory(equilibrium_result, trajectory,
                                     trajectory_filename)

    return equilibrium_result
Beispiel #2
0
def run_equilibrium(equilibrium_result: EquilibriumResult, thermodynamic_state: states.ThermodynamicState,
                    nsteps_equil: int, topology: md.Topology, n_iterations : int,
                    atom_indices_to_save: List[int] = None, trajectory_filename: str = None, splitting: str="V R O R V", timestep: unit.Quantity=1.0*unit.femtoseconds) -> EquilibriumResult:
    """
    Run nsteps of equilibrium sampling at the specified thermodynamic state and return the final sampler state
    as well as a trajectory of the positions after each application of an MCMove. This means that if the MCMove
    is configured to run 1000 steps of dynamics, and n_iterations is 100, there will be 100 frames in the resulting
    trajectory; these are the result of 100,000 steps (1000*100) of dynamics.

    Parameters
    ----------
    equilibrium_result : EquilibriumResult
       EquilibriumResult namedtuple containing the information necessary to resume
    thermodynamic_state : openmmtools.states.ThermodynamicState
        The thermodynamic state (including context parameters) that should be used
    nsteps_equil : int
        The number of equilibrium steps that a move should make when apply is called
    topology : mdtraj.Topology
        an MDTraj topology object used to construct the trajectory
    n_iterations : int
        The number of times to apply the move. Note that this is not the number of steps of dynamics; it is
        n_iterations*n_steps (which is set in the MCMove).
    splitting: str, default "V R O H R V"
        The splitting string for the dynamics
    atom_indices_to_save : list of int, default None
        list of indices to save (when excluding waters, for instance). If None, all indices are saved.
    trajectory_filename : str, optional, default None
        Full filepath of trajectory files. If none, trajectory files are not written.
    splitting: str, default "V R O H R V"
        The splitting string for the dynamics
    Returns
    -------
    equilibrium_result : EquilibriumResult
        Container namedtuple that has the SamplerState for resuming, an MDTraj trajectory, and the reduced potential of the
        final frame.
    """
    sampler_state = equilibrium_result.sampler_state
    #get the atom indices we need to subset the topology and positions
    if atom_indices_to_save is None:
        atom_indices = list(range(topology.n_atoms))
        subset_topology = topology
    else:
        subset_topology = topology.subset(atom_indices_to_save)
        atom_indices = atom_indices_to_save

    n_atoms = subset_topology.n_atoms

    #construct the MCMove:
    mc_move = mcmc.LangevinSplittingDynamicsMove(n_steps=nsteps_equil, splitting=splitting)
    mc_move.n_restart_attempts = 10

    #create a numpy array for the trajectory
    trajectory_positions = np.zeros([n_iterations, n_atoms, 3])
    trajectory_box_lengths = np.zeros([n_iterations, 3])
    trajectory_box_angles = np.zeros([n_iterations, 3])

    #loop through iterations and apply MCMove, then collect positions into numpy array
    for iteration in range(n_iterations):
        mc_move.apply(thermodynamic_state, sampler_state)

        trajectory_positions[iteration, :] = sampler_state.positions[atom_indices, :].value_in_unit_system(unit.md_unit_system)

        #get the box lengths and angles
        a, b, c, alpha, beta, gamma = mdtrajutils.unitcell.box_vectors_to_lengths_and_angles(*sampler_state.box_vectors)
        trajectory_box_lengths[iteration, :] = [a, b, c]
        trajectory_box_angles[iteration, :] = [alpha, beta, gamma]

    #construct trajectory object:
    trajectory = md.Trajectory(trajectory_positions, subset_topology, unitcell_lengths=trajectory_box_lengths, unitcell_angles=trajectory_box_angles)

    #get the reduced potential from the final frame for endpoint perturbations
    reduced_potential_final_frame = thermodynamic_state.reduced_potential(sampler_state)

    #construct equilibrium result object
    equilibrium_result = EquilibriumResult(sampler_state, reduced_potential_final_frame)

    #If there is a trajectory filename passed, write out the results here:
    if trajectory_filename is not None:
        write_equilibrium_trajectory(equilibrium_result, trajectory, trajectory_filename)

    return equilibrium_result
Beispiel #3
0
def run_protocol(equilibrium_result: EquilibriumResult,
                 thermodynamic_state: states.ThermodynamicState,
                 alchemical_functions: dict,
                 nstep_neq: int,
                 topology: md.Topology,
                 work_save_interval: int,
                 splitting: str = "V R O H R V",
                 atom_indices_to_save: List[int] = None,
                 trajectory_filename: str = None,
                 write_configuration: bool = False,
                 timestep: unit.Quantity = 1.0 * unit.femtoseconds,
                 measure_shadow_work: bool = False) -> NonequilibriumResult:
    """
    Perform a nonequilibrium switching protocol and return the nonequilibrium protocol work. Note that it is expected
    that this will perform an entire protocol, that is, switching lambda completely from 0 to 1, in increments specified
    by the ne_mc_move. The trajectory that results, along with the work values, will contain n_iterations elements.

    Parameters
    ----------
    equilibrium_result : EquilibriumResult namedtuple
        The result of an equilibrium simulation
    thermodynamic_state : openmmtools.states.ThermodynamicState
        The thermodynamic state at which to run the protocol
    alchemical_functions : dict
        The alchemical functions to use for switching
    nstep_neq : int
        The number of nonequilibrium steps in the protocol
    topology : mdtraj.Topology
        An MDtraj topology for the system to generate trajectories
    work_save_interval : int
        How often to write the work and, if requested, configurations
    splitting : str, default "V R O H R V"
        The splitting string to use for the Langevin integration
    atom_indices_to_save : list of int, default None
        list of indices to save (when excluding waters, for instance). If None, all indices are saved.
    trajectory_filename : str, default None
        Full filepath of output trajectory, if desired. If None, no trajectory file is written.
    write_configuration : bool, default False
        Whether to also write configurations of the trajectory at the requested interval.
    timestep : unit.Quantity, default 1 fs
        The timestep to use in the integrator
    Returns
    -------
    nonequilibrium_result : NonequilibriumResult
        result object containing the trajectory of the nonequilibrium calculation, as well as the cumulative work
        for each frame.
    """
    #get the sampler state needed for the simulation
    sampler_state = equilibrium_result.sampler_state
    temperature = thermodynamic_state.temperature

    #get the atom indices we need to subset the topology and positions
    if atom_indices_to_save is None:
        atom_indices = list(range(topology.n_atoms))
        subset_topology = topology
    else:
        subset_topology = topology.subset(atom_indices_to_save)
        atom_indices = atom_indices_to_save

    ne_mc_move = NonequilibriumSwitchingMove(
        alchemical_functions,
        splitting,
        temperature,
        nstep_neq,
        timestep,
        work_save_interval,
        subset_topology,
        atom_indices,
        save_configuration=write_configuration,
        measure_shadow_work=measure_shadow_work)

    ne_mc_move.reset()

    #apply the nonequilibrium move
    ne_mc_move.apply(thermodynamic_state, sampler_state)

    #get the cumulative work
    cumulative_work = ne_mc_move.cumulative_work

    #get the protocol work
    protocol_work = ne_mc_move.protocol_work

    #if we're measuring shadow work, get that. Otherwise just fill in zeros:
    if measure_shadow_work:
        shadow_work = ne_mc_move.shadow_work
    else:
        shadow_work = np.zeros_like(protocol_work)

    #create a result object and return that
    nonequilibrium_result = NonequilibriumResult(cumulative_work,
                                                 protocol_work, shadow_work)

    #if desired, write nonequilibrium trajectories:
    if trajectory_filename is not None:
        #to get the filename for cumulative work, replace the extension of the trajectory file with .cw.npy
        filepath_parts = trajectory_filename.split(".")
        cw_filepath_parts = copy.deepcopy(filepath_parts)
        pw_filepath_parts = copy.deepcopy(filepath_parts)
        if measure_shadow_work:
            sw_filepath_parts = copy.deepcopy(filepath_parts)
            sw_filepath_parts[-1] = "sw.npy"
            shad_work_filepath = ".".join(sw_filepath_parts)

        cw_filepath_parts[-1] = "cw.npy"
        pw_filepath_parts[-1] = "pw.npy"

        cum_work_filepath = ".".join(cw_filepath_parts)
        prot_work_filepath = ".".join(pw_filepath_parts)

        #if writing configurations was requested, get the trajectory
        if write_configuration:
            try:
                trajectory = ne_mc_move.trajectory
                write_nonequilibrium_trajectory(nonequilibrium_result,
                                                trajectory,
                                                trajectory_filename)
            except NoTrajectoryException:
                pass

        np.save(cum_work_filepath, nonequilibrium_result.cumulative_work)
        np.save(prot_work_filepath, nonequilibrium_result.protocol_work)

        if measure_shadow_work:
            np.save(shad_work_filepath, shadow_work)

    return nonequilibrium_result
Beispiel #4
0
def run_protocol(equilibrium_result: EquilibriumResult, thermodynamic_state: states.ThermodynamicState,
                 alchemical_functions: dict, nstep_neq: int, topology: md.Topology, work_save_interval: int, splitting: str="V R O H R V",
                 atom_indices_to_save: List[int] = None, trajectory_filename: str = None, write_configuration: bool = False, timestep: unit.Quantity=1.0*unit.femtoseconds, measure_shadow_work: bool=False) -> NonequilibriumResult:
    """
    Perform a nonequilibrium switching protocol and return the nonequilibrium protocol work. Note that it is expected
    that this will perform an entire protocol, that is, switching lambda completely from 0 to 1, in increments specified
    by the ne_mc_move. The trajectory that results, along with the work values, will contain n_iterations elements.

    Parameters
    ----------
    equilibrium_result : EquilibriumResult namedtuple
        The result of an equilibrium simulation
    thermodynamic_state : openmmtools.states.ThermodynamicState
        The thermodynamic state at which to run the protocol
    alchemical_functions : dict
        The alchemical functions to use for switching
    nstep_neq : int
        The number of nonequilibrium steps in the protocol
    topology : mdtraj.Topology
        An MDtraj topology for the system to generate trajectories
    work_save_interval : int
        How often to write the work and, if requested, configurations
    splitting : str, default "V R O H R V"
        The splitting string to use for the Langevin integration
    atom_indices_to_save : list of int, default None
        list of indices to save (when excluding waters, for instance). If None, all indices are saved.
    trajectory_filename : str, default None
        Full filepath of output trajectory, if desired. If None, no trajectory file is written.
    write_configuration : bool, default False
        Whether to also write configurations of the trajectory at the requested interval.
    timestep : unit.Quantity, default 1 fs
        The timestep to use in the integrator
    Returns
    -------
    nonequilibrium_result : NonequilibriumResult
        result object containing the trajectory of the nonequilibrium calculation, as well as the cumulative work
        for each frame.
    """
    #get the sampler state needed for the simulation
    sampler_state = equilibrium_result.sampler_state
    temperature = thermodynamic_state.temperature

    #get the atom indices we need to subset the topology and positions
    if atom_indices_to_save is None:
        atom_indices = list(range(topology.n_atoms))
        subset_topology = topology
    else:
        subset_topology = topology.subset(atom_indices_to_save)
        atom_indices = atom_indices_to_save

    ne_mc_move = NonequilibriumSwitchingMove(alchemical_functions, splitting, temperature, nstep_neq, timestep, work_save_interval, subset_topology, atom_indices, save_configuration=write_configuration, measure_shadow_work=measure_shadow_work)

    ne_mc_move.reset()

    #apply the nonequilibrium move
    ne_mc_move.apply(thermodynamic_state, sampler_state)

    #get the cumulative work
    cumulative_work = ne_mc_move.cumulative_work

    #get the protocol work
    protocol_work = ne_mc_move.protocol_work

    #if we're measuring shadow work, get that. Otherwise just fill in zeros:
    if measure_shadow_work:
        shadow_work = ne_mc_move.shadow_work
    else:
        shadow_work = np.zeros_like(protocol_work)

    #create a result object and return that
    nonequilibrium_result = NonequilibriumResult(cumulative_work, protocol_work, shadow_work)

    #if desired, write nonequilibrium trajectories:
    if trajectory_filename is not None:
        #to get the filename for cumulative work, replace the extension of the trajectory file with .cw.npy
        filepath_parts = trajectory_filename.split(".")
        cw_filepath_parts = copy.deepcopy(filepath_parts)
        pw_filepath_parts = copy.deepcopy(filepath_parts)
        if measure_shadow_work:
            sw_filepath_parts = copy.deepcopy(filepath_parts)
            sw_filepath_parts[-1] = "sw.npy"
            shad_work_filepath = ".".join(sw_filepath_parts)

        cw_filepath_parts[-1] = "cw.npy"
        pw_filepath_parts[-1] = "pw.npy"

        cum_work_filepath = ".".join(cw_filepath_parts)
        prot_work_filepath = ".".join(pw_filepath_parts)

        #if writing configurations was requested, get the trajectory
        if write_configuration:
            try:
                trajectory = ne_mc_move.trajectory
                write_nonequilibrium_trajectory(nonequilibrium_result, trajectory, trajectory_filename)
            except NoTrajectoryException:
                pass

        np.save(cum_work_filepath, nonequilibrium_result.cumulative_work)
        np.save(prot_work_filepath, nonequilibrium_result.protocol_work)

        if measure_shadow_work:
            np.save(shad_work_filepath, shadow_work)

    return nonequilibrium_result