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
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
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
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