def alchemical_factory_check(reference_system, positions, platform_name=None, precision=None, factory_args=None): """ Compare energies of reference system and fully-interacting alchemically modified system. ARGUMENTS reference_system : simtk.openmm.System The reference System object to compare with positions : simtk.unit.Quantity of dimentsion [natoms,3] with units compatible with angstroms The positions to assess energetics for precision : str, optional, default=None Precision model, or default if not specified. ('single', 'double', 'mixed') factory_args : dict(), optional, default=None Arguments passed to AbsoluteAlchemicalFactory. """ # Create a factory to produce alchemical intermediates. logger.info("Creating alchemical factory...") initial_time = time.time() factory = AbsoluteAlchemicalFactory(reference_system, **factory_args) final_time = time.time() elapsed_time = final_time - initial_time logger.info("AbsoluteAlchemicalFactory initialization took %.3f s" % elapsed_time) platform = None if platform_name: platform = openmm.Platform.getPlatformByName(platform_name) alchemical_system = factory.createPerturbedSystem() compareSystemEnergies(positions, [reference_system, alchemical_system], ['reference', 'alchemical'], platform=platform, precision=precision) return
def compare_platforms(system, positions, factory_args=dict()): # Create annihilated version of vacuum system. factory = AbsoluteAlchemicalFactory(system, **factory_args) alchemical_system = factory.createPerturbedSystem() def set_lambda(alchemical_system, lambda_value): alchemical_state = AlchemicalState(lambda_electrostatics=lambda_value, lambda_sterics=lambda_value, lambda_torsions=lambda_value) AbsoluteAlchemicalFactory.perturbSystem(alchemical_system, alchemical_state) # Compare energies energies = dict() platform_names = list() for platform_index in range(openmm.Platform.getNumPlatforms()): platform = openmm.Platform.getPlatform(platform_index) platform_name = platform.getName() if platform_name != 'Reference': platform_names.append(platform_name) energies[platform_name] = dict() energies[platform_name]['full'] = compute_energy(system, positions, platform=platform) set_lambda(alchemical_system, 1.0) energies[platform_name]['lambda = 1'] = compute_energy(alchemical_system, positions, platform=platform) set_lambda(alchemical_system, 0.0) energies[platform_name]['lambda = 0'] = compute_energy(alchemical_system, positions, platform=platform) # Check deviations. for platform_name in platform_names: for energy_name in ['full', 'lambda = 1', 'lambda = 0']: delta = energies[platform_name][energy_name] - energies['Reference'][energy_name] if (abs(delta) > MAX_DELTA): raise Exception("Maximum allowable deviation on platform %s exceeded (was %.8f kcal/mol; allowed %.8f kcal/mol); test failed." % (platform_name, delta / unit.kilocalories_per_mole, MAX_DELTA / unit.kilocalories_per_mole))
def test_softcore_parameters(): """ Testing alchemical slave functions """ alchemical_functions = { 'lambda_sterics' : 'lambda', 'lambda_electrostatics' : 'lambda', 'lambda_bonds' : 'lambda', 'lambda_angles' : 'lambda', 'lambda_torsions' : 'lambda' } name = 'Lennard-Jones fluid with dispersion correction' test_system = copy.deepcopy(test_systems[name]) reference_system = test_system['test'].system positions = test_system['test'].positions factory_args = test_system['factory_args'] factory_args.update({ 'softcore_alpha' : 1.0, 'softcore_beta' : 1.0, 'softcore_a' : 1.0, 'softcore_b' : 1.0, 'softcore_c' : 1.0, 'softcore_d' : 1.0, 'softcore_e' : 1.0, 'softcore_f' : 1.0 }) factory = AbsoluteAlchemicalFactory(reference_system, **factory_args) alchemical_system = factory.createPerturbedSystem() compareSystemEnergies(positions, [reference_system, alchemical_system], ['reference', 'alchemical'])
def alchemical_factory_check( reference_system, positions, receptor_atoms, ligand_atoms, platform_name=None, annihilate_electrostatics=True, annihilate_sterics=False, precision=None, ): """ Compare energies of reference system and fully-interacting alchemically modified system. ARGUMENTS reference_system (simtk.openmm.System) - the reference System object to compare with positions - the positions to assess energetics for receptor_atoms (list of int) - the list of receptor atoms ligand_atoms (list of int) - the list of ligand atoms to alchemically modify precision : str, optional, default=None Precision model, or default if not specified. ('single', 'double', 'mixed') """ # Create a factory to produce alchemical intermediates. logger.info("Creating alchemical factory...") initial_time = time.time() factory = AbsoluteAlchemicalFactory( reference_system, ligand_atoms=ligand_atoms, annihilate_electrostatics=annihilate_electrostatics, annihilate_sterics=annihilate_sterics, ) final_time = time.time() elapsed_time = final_time - initial_time logger.info("AbsoluteAlchemicalFactory initialization took %.3f s" % elapsed_time) platform = None if platform_name: platform = openmm.Platform.getPlatformByName(platform_name) alchemical_system = factory.createPerturbedSystem() compareSystemEnergies( positions, [reference_system, alchemical_system], ["reference", "alchemical"], platform=platform, precision=precision, ) return
def test_ncmc_alchemical_integrator_stability_molecules(): """ Test NCMCAlchemicalIntegrator """ molecule_names = ['pentane', 'biphenyl', 'imatinib'] #if os.environ.get("TRAVIS", None) == 'true': # molecule_names = ['pentane'] for molecule_name in molecule_names: from perses.tests.utils import createSystemFromIUPAC [molecule, system, positions, topology] = createSystemFromIUPAC(molecule_name) # Eliminate half of the molecule # TODO: Use a more rigorous scheme to make sure we are really cutting the molecule in half and not just eliminating hydrogens or something. alchemical_atoms = [ index for index in range(int(system.getNumParticles()/2)) ] # Create an alchemically-modified system. from alchemy import AbsoluteAlchemicalFactory alchemical_factory = AbsoluteAlchemicalFactory(system, ligand_atoms=alchemical_atoms, annihilate_electrostatics=True, annihilate_sterics=True) # Return the alchemically-modified system in fully-interacting form. alchemical_system = alchemical_factory.createPerturbedSystem() # Create an NCMC switching integrator. from perses.annihilation.ncmc_switching import NCMCVVAlchemicalIntegrator temperature = 300.0 * unit.kelvin nsteps = 10 # number of steps to run integration for functions = { 'lambda_sterics' : 'lambda', 'lambda_electrostatics' : 'lambda^0.5', 'lambda_torsions' : 'lambda', 'lambda_angles' : 'lambda^2' } ncmc_integrator = NCMCVVAlchemicalIntegrator(temperature, alchemical_system, functions, direction='delete', nsteps=nsteps, timestep=1.0*unit.femtoseconds) # Create a Context context = openmm.Context(alchemical_system, ncmc_integrator) context.setPositions(positions) # Run the integrator ncmc_integrator.step(nsteps) # Check positions are finite positions = context.getState(getPositions=True).getPositions(asNumpy=True) if np.isnan(np.any(positions / positions.unit)): raise Exception('NCMCAlchemicalIntegrator gave NaN positions') if np.isnan(ncmc_integrator.getLogAcceptanceProbability(context)): raise Exception('NCMCAlchemicalIntegrator gave NaN logAcceptanceProbability') del context, ncmc_integrator
def make_alchemical_system(self, topology_proposal, direction='insert'): """ Generate an alchemically-modified system at the correct atoms based on the topology proposal Arguments --------- topology_proposal : TopologyProposal namedtuple Contains old topology, proposed new topology, and atom mapping direction : str, optional, default='insert' Direction of topology proposal to use for identifying alchemical atoms (allowed values: ['insert', 'delete']) Returns ------- unmodified_system : simtk.openmm.System Unmodified real system corresponding to appropriate leg of transformation. alchemical_system : simtk.openmm.System The system with appropriate atoms alchemically modified """ if direction not in ['insert', 'delete']: raise Exception("'direction' must be one of ['insert', 'delete']; was '%s' instead" % direction) atom_map = topology_proposal.new_to_old_atom_map #take the unique atoms as those not in the {new_atom : old_atom} atom map if direction == 'delete': unmodified_system = topology_proposal.old_system alchemical_atoms = [atom for atom in range(unmodified_system.getNumParticles()) if atom not in atom_map.values()] elif direction == 'insert': unmodified_system = topology_proposal.new_system alchemical_atoms = [atom for atom in range(unmodified_system.getNumParticles()) if atom not in atom_map.keys()] else: raise Exception("direction must be one of ['delete', 'insert']; found '%s' instead" % direction) # DEBUG #print('alchemical atoms:') #print(alchemical_atoms) # Create an alchemical factory. from alchemy import AbsoluteAlchemicalFactory alchemical_factory = AbsoluteAlchemicalFactory(unmodified_system, ligand_atoms=alchemical_atoms, annihilate_electrostatics=True, annihilate_sterics=True, alchemical_bonds=None, alchemical_angles=None) # Return the alchemically-modified system in fully-interacting form. alchemical_system = alchemical_factory.createPerturbedSystem() return [unmodified_system, alchemical_system]
def check_waterbox(platform=None, precision=None, nonbondedMethod=openmm.NonbondedForce.CutoffPeriodic): """Compare annihilated states in vacuum and a large box. """ platform_name = platform.getName() from openmmtools import testsystems testsystem = testsystems.WaterBox() system = testsystem.system positions = testsystem.positions # Use reaction field for force in system.getForces(): if force.__class__.__name__ == 'NonbondedForce': force.setNonbondedMethod(nonbondedMethod) factory_args = {'ligand_atoms' : [], 'receptor_atoms' : [], 'annihilate_sterics' : False, 'annihilate_electrostatics' : True } # Create alchemically-modified system factory = AbsoluteAlchemicalFactory(system, **factory_args) alchemical_system = factory.createPerturbedSystem() # Compare energies system_energy = compute_energy(system, positions, platform=platform, precision=precision) alchemical_1_energy = compute_energy(alchemical_system, positions, platform=platform, precision=precision) # Set lambda = 0 lambda_value = 0.0 alchemical_state = AlchemicalState(lambda_electrostatics=lambda_value, lambda_sterics=lambda_value, lambda_torsions=lambda_value) AbsoluteAlchemicalFactory.perturbSystem(alchemical_system, alchemical_state) alchemical_0_energy = compute_energy(alchemical_system, positions, platform=platform, precision=precision) # Check deviation. logger.info("========") logger.info("Platform %s" % platform_name) logger.info("Alchemically-modified WaterBox with no alchemical atoms") logger.info('real system : %8.3f kcal/mol' % (system_energy / unit.kilocalories_per_mole)) logger.info('lambda = 1 : %8.3f kcal/mol' % (alchemical_1_energy / unit.kilocalories_per_mole)) logger.info('lambda = 0 : %8.3f kcal/mol' % (alchemical_0_energy / unit.kilocalories_per_mole)) delta = alchemical_1_energy - alchemical_0_energy logger.info("ERROR : %8.3f kcal/mol" % (delta / unit.kilocalories_per_mole)) if (abs(delta) > MAX_DELTA): raise Exception("Maximum allowable deviation on platform %s exceeded (was %.8f kcal/mol; allowed %.8f kcal/mol); test failed." % (platform_name, delta / unit.kilocalories_per_mole, MAX_DELTA / unit.kilocalories_per_mole))
def make_alchemical_system(self, unmodified_system, topology_proposal, direction='create'): """ Generate an alchemically-modified system at the correct atoms based on the topology proposal Arguments --------- unmodified_system : openmm.System object The unmodified system to get alchemical modifications topology_proposal : TopologyProposal namedtuple Contains old topology, proposed new topology, and atom mapping direction : str, optional, default='insert' Direction of topology proposal to use for identifying alchemical atoms. Returns ------- alchemical_system : openmm.System object The system with appropriate atoms alchemically modified """ atom_map = topology_proposal.new_to_old_atom_map n_atoms = unmodified_system.getNumParticles() #take the unique atoms as those not in the {new_atom : old_atom} atom map if direction == 'delete': alchemical_atoms = [atom for atom in range(n_atoms) if atom not in atom_map.values()] elif direction == 'create': alchemical_atoms = [atom for atom in range(n_atoms) if atom not in atom_map.keys()] else: raise Exception("direction must be one of ['delete', 'create']; found '%s' instead" % direction) logging.debug(alchemical_atoms) # Create an alchemical factory. from alchemy import AbsoluteAlchemicalFactory alchemical_factory = AbsoluteAlchemicalFactory(unmodified_system, ligand_atoms=alchemical_atoms) # Return the alchemically-modified system. alchemical_system = alchemical_factory.createPerturbedSystem() return alchemical_system
# Identify ligand indices by residue name print('Identifying ligand atoms to be alchemically modified...') reference = md.load(pdb_filename) alchemical_atoms = reference.topology.select(ligand_dsl_selection) # these atoms will be alchemically softened alchemical_atoms = [ int(index) for index in alchemical_atoms ] # recode as Python int print("MDTraj DSL selection '%s' identified %d atoms" % (ligand_dsl_selection, len(alchemical_atoms))) # Create alchemically-modified system using fused softcore electrostatics and sterics print('Creating alchemically modified system...') print('lambda schedule: %s' % str(alchemical_lambdas)) from alchemy import AbsoluteAlchemicalFactory from sams import ThermodynamicState thermodynamic_states = list() factory = AbsoluteAlchemicalFactory(reference_system, ligand_atoms=alchemical_atoms, annihilate_electrostatics=True, annihilate_sterics=False) system = factory.createPerturbedSystem() for alchemical_lambda in alchemical_lambdas: parameters = {'lambda_sterics' : alchemical_lambda, 'lambda_electrostatics' : alchemical_lambda} thermodynamic_states.append( ThermodynamicState(system=system, temperature=temperature, pressure=pressure, parameters=parameters) ) # Select platform automatically; use mixed precision integrator = openmm.VerletIntegrator(timestep) context = openmm.Context(system, integrator) platform = context.getPlatform() del context platform.setPropertyDefaultValue('Precision', 'mixed') if platform.getName() == 'OpenCL': # GTX-1080 workaround platform.setPropertyDefaultValue('OpenCLDisablePmeStream', 'true') # Minimize
def __init__(self, alchemical_protocol='two-phase', nlambda=50, **kwargs): """ Create an alchemical free energy calculation SAMS test system from the provided system. Parameters ---------- alchemical_protocol : str, optional, default='two-phase' Alchemical protocol scheme to use. ['two-phase', 'fused'] nlambda : int, optional, default=50 Number of alchemical states. """ super(AlchemicalSAMSTestSystem, self).__init__(**kwargs) self.description = 'Alchemical SAMS test system' self.alchemical_protocol = alchemical_protocol if not (hasattr(self, 'topology') and hasattr(self, 'system') and hasattr(self, 'positions') and hasattr(self, 'alchemical_atoms')): raise Exception("%s: 'topology', 'system', 'positions', and 'alchemical_atoms' properties must be defined!" % self.__class__.__name__) if not hasattr(self, 'temperature'): self.temperature = 300 * unit.kelvin if not hasattr(self, 'temperature'): self.temperature = 300 * unit.kelvin if not hasattr(self, 'pressure'): self.pressure = None # Add a MonteCarloBarostat if system does not have one has_barostat = False for force in self.system.getForces(): if force.__class__.__name__ in ['MonteCarloBarostat', 'MonteCarloAnisotropicBarostat']: has_barostat = True if (self.pressure is not None) and (not has_barostat): barostat = openmm.MonteCarloBarostat(self.pressure, self.temperature) self.system.addForce(barostat) # Create alchemically-modified system and populate thermodynamic states. from alchemy import AbsoluteAlchemicalFactory from sams import ThermodynamicState self.thermodynamic_states = list() if alchemical_protocol == 'fused': factory = AbsoluteAlchemicalFactory(self.system, ligand_atoms=self.alchemical_atoms, annihilate_electrostatics=True, annihilate_sterics=False) self.system = factory.createPerturbedSystem() from sams import ThermodynamicState alchemical_lambdas = np.linspace(1.0, 0.0, nlambda) for alchemical_lambda in alchemical_lambdas: parameters = {'lambda_sterics' : alchemical_lambda, 'lambda_electrostatics' : alchemical_lambda} self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=self.temperature, pressure=self.pressure, parameters=parameters) ) elif alchemical_protocol == 'two-phase': factory = AbsoluteAlchemicalFactory(self.system, ligand_atoms=self.alchemical_atoms, annihilate_electrostatics=True, annihilate_sterics=False, softcore_beta=0.0) # turn off softcore electrostatics self.system = factory.createPerturbedSystem() nelec = int(nlambda/2.0) nvdw = nlambda - nelec for state in range(nelec+1): parameters = {'lambda_sterics' : 1.0, 'lambda_electrostatics' : (1.0 - float(state)/float(nelec)) } self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=self.temperature, pressure=self.pressure, parameters=parameters) ) for state in range(1,nvdw+1): parameters = {'lambda_sterics' : (1.0 - float(state)/float(nvdw)), 'lambda_electrostatics' : 0.0 } self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=self.temperature, pressure=self.pressure, parameters=parameters) ) else: raise Exception("'alchemical_protocol' must be one of ['two-phase', 'fused']; scheme '%s' unknown." % alchemical_protocol) # Create SAMS samplers print('Setting up samplers...') from sams.samplers import SamplerState, MCMCSampler, ExpandedEnsembleSampler, SAMSSampler thermodynamic_state_index = 0 # initial thermodynamic state index thermodynamic_state = self.thermodynamic_states[thermodynamic_state_index] sampler_state = SamplerState(positions=self.positions) self.mcmc_sampler = MCMCSampler(sampler_state=sampler_state, thermodynamic_state=thermodynamic_state, ncfile=self.ncfile) self.mcmc_sampler.timestep = 2.0 * unit.femtoseconds self.mcmc_sampler.nsteps = 500 #self.mcmc_sampler.pdbfile = open('output.pdb', 'w') self.mcmc_sampler.topology = self.topology self.mcmc_sampler.verbose = True self.exen_sampler = ExpandedEnsembleSampler(self.mcmc_sampler, self.thermodynamic_states) self.exen_sampler.verbose = True self.sams_sampler = SAMSSampler(self.exen_sampler) self.sams_sampler.verbose = True # DEBUG: Write PDB of initial frame from simtk.openmm.app import PDBFile outfile = open('initial.pdb', 'w') PDBFile.writeFile(self.topology, self.positions, outfile) outfile.close()
def __init__(self, **kwargs): super(LoopSoftening, self).__init__(**kwargs) self.description = 'Alchemical Loop Softening script' padding = 9.0*unit.angstrom explicit_solvent_model = 'tip3p' setup_path = 'data/mtor' # Create topology, positions, and system. from pkg_resources import resource_filename gaff_xml_filename = resource_filename('sams', 'data/gaff.xml') system_generators = dict() ffxmls = [gaff_xml_filename, 'amber99sbildn.xml', 'tip3p.xml'] forcefield_kwargs={ 'nonbondedMethod' : app.CutoffPeriodic, 'nonbondedCutoff' : 9.0 * unit.angstrom, 'implicitSolvent' : None, 'constraints' : app.HBonds, 'rigidWater' : True } # Load topologies and positions for all components print('Creating mTOR test system...') forcefield = app.ForceField(*ffxmls) from simtk.openmm.app import PDBFile, Modeller pdb_filename = resource_filename('sams', os.path.join(setup_path, 'mtor_pdbfixer_apo.pdb')) pdbfile = PDBFile(pdb_filename) modeller = app.Modeller(pdbfile.topology, pdbfile.positions) print('Adding solvent...') modeller.addSolvent(forcefield, model=explicit_solvent_model, padding=padding) self.topology = modeller.getTopology() self.positions = modeller.getPositions() print('Creating system...') self.system = forcefield.createSystem(self.topology, **forcefield_kwargs) # DEBUG: Write PDB outfile = open('initial.pdb', 'w') PDBFile.writeFile(self.topology, self.positions, outfile) outfile.close() # Atom Selection using MDtraj res_pairs = [[403, 483], [1052, 1109]] t = md.load(pdb_filename) alchemical_atoms = set() for x in res_pairs: start = min(t.top.select('residue %s' % min(x))) end = max(t.top.select('residue %s' % max(x))) + 1 alchemical_atoms.union(set(range(start, end))) # Create thermodynamic states. print('Creating alchemically-modified system...') temperature = 300 * unit.kelvin pressure = 1.0 * unit.atmospheres from alchemy import AbsoluteAlchemicalFactory factory = AbsoluteAlchemicalFactory(self.system, ligand_atoms=alchemical_atoms, annihilate_electrostatics=True, alchemical_torsions=True, annihilate_sterics=True, softcore_beta=0.0) # turn off softcore electrostatics self.system = factory.createPerturbedSystem() print('Setting up alchemical intermediates...') from sams import ThermodynamicState self.thermodynamic_states = list() for state in range(26): parameters = {'lambda_sterics' : 1.0, 'lambda_electrostatics' : (1.0 - float(state)/25.0) } self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=temperature, parameters=parameters) ) for state in range(1,26): parameters = {'lambda_sterics' : (1.0 - float(state)/25.0), 'lambda_electrostatics' : 0.0 } self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=temperature, parameters=parameters) ) #minimize(self.system, self.positions) minimize(self.system) # Create SAMS samplers print('Setting up samplers...') from sams.samplers import SamplerState, MCMCSampler, ExpandedEnsembleSampler, SAMSSampler thermodynamic_state_index = 0 # initial thermodynamic state index thermodynamic_state = self.thermodynamic_states[thermodynamic_state_index] sampler_state = SamplerState(positions=self.system.positions) self.mcmc_sampler = MCMCSampler(sampler_state=sampler_state, thermodynamic_state=thermodynamic_state, ncfile=self.ncfile) self.mcmc_sampler.pdbfile = open('output.pdb', 'w') self.mcmc_sampler.topology = self.topology self.mcmc_sampler.verbose = True self.exen_sampler = ExpandedEnsembleSampler(self.mcmc_sampler, self.thermodynamic_states) self.exen_sampler.verbose = True self.sams_sampler = SAMSSampler(self.exen_sampler) self.sams_sampler.verbose = True
def test_overlap(): """ BUGS TO REPORT: * Even if epsilon = 0, energy of two overlapping atoms is 'nan'. * Periodicity in 'nan' if dr = 0.1 even in nonperiodic system """ # Create a reference system. import testsystems print "Creating Lennard-Jones cluster system..." #[reference_system, coordinates] = testsystems.LennardJonesFluid() #receptor_atoms = [0] #ligand_atoms = [1] [reference_system, coordinates] = testsystems.LysozymeImplicit() receptor_atoms = range(0,2603) # T4 lysozyme L99A ligand_atoms = range(2603,2621) # p-xylene import simtk.unit as units unit = coordinates.unit coordinates = units.Quantity(numpy.array(coordinates / unit), unit) factory = AbsoluteAlchemicalFactory(reference_system, ligand_atoms=ligand_atoms) alchemical_state = AlchemicalState(0.00, 0.00, 0.00, 1.0) # Create the perturbed system. print "Creating alchemically-modified state..." alchemical_system = factory.createPerturbedSystem(alchemical_state) # Compare energies. import simtk.unit as units import simtk.openmm as openmm timestep = 1.0 * units.femtosecond print "Computing reference energies..." integrator = openmm.VerletIntegrator(timestep) context = openmm.Context(reference_system, integrator) context.setPositions(coordinates) state = context.getState(getEnergy=True) reference_potential = state.getPotentialEnergy() del state, context, integrator print reference_potential print "Computing alchemical energies..." integrator = openmm.VerletIntegrator(timestep) context = openmm.Context(alchemical_system, integrator) dr = 0.1 * units.angstroms # TODO: Why does 0.1 cause periodic 'nan's? a = receptor_atoms[-1] b = ligand_atoms[-1] delta = coordinates[a,:] - coordinates[b,:] for k in range(3): coordinates[ligand_atoms,k] += delta[k] for i in range(30): r = dr * i coordinates[ligand_atoms,0] += dr context.setPositions(coordinates) state = context.getState(getEnergy=True) alchemical_potential = state.getPotentialEnergy() print "%8.3f A : %f " % (r / units.angstroms, alchemical_potential / units.kilocalories_per_mole) del state, context, integrator return
def testAlchemicalFactory(reference_system, coordinates, receptor_atoms, ligand_atoms, platform_name='Reference', annihilateElectrostatics=True, annihilateLennardJones=False): """ Compare energies of reference system and fully-interacting alchemically modified system. ARGUMENTS reference_system (simtk.openmm.System) - the reference System object to compare with coordinates - the coordinates to assess energetics for receptor_atoms (list of int) - the list of receptor atoms ligand_atoms (list of int) - the list of ligand atoms to alchemically modify """ import simtk.unit as units import simtk.openmm as openmm import time # Create a factory to produce alchemical intermediates. print "Creating alchemical factory..." initial_time = time.time() factory = AbsoluteAlchemicalFactory(reference_system, ligand_atoms=ligand_atoms) final_time = time.time() elapsed_time = final_time - initial_time print "AbsoluteAlchemicalFactory initialization took %.3f s" % elapsed_time # Create an alchemically-perturbed state corresponding to nearly fully-interacting. # NOTE: We use a lambda slightly smaller than 1.0 because the AlchemicalFactory does not use Custom*Force softcore versions if lambda = 1.0 identically. lambda_value = 1.0 - 1.0e-6 alchemical_state = AlchemicalState(0.00, lambda_value, lambda_value, lambda_value) alchemical_state.annihilateElectrostatics = annihilateElectrostatics alchemical_state.annihilateLennardJones = annihilateLennardJones #platform_name = 'Reference' # DEBUG platform = openmm.Platform.getPlatformByName(platform_name) # Create the perturbed system. print "Creating alchemically-modified state..." initial_time = time.time() alchemical_system = factory.createPerturbedSystem(alchemical_state) final_time = time.time() elapsed_time = final_time - initial_time # Compare energies. timestep = 1.0 * units.femtosecond print "Computing reference energies..." reference_integrator = openmm.VerletIntegrator(timestep) reference_context = openmm.Context(reference_system, reference_integrator, platform) reference_context.setPositions(coordinates) reference_state = reference_context.getState(getEnergy=True) reference_potential = reference_state.getPotentialEnergy() print "Computing alchemical energies..." alchemical_integrator = openmm.VerletIntegrator(timestep) alchemical_context = openmm.Context(alchemical_system, alchemical_integrator, platform) alchemical_context.setPositions(coordinates) alchemical_state = alchemical_context.getState(getEnergy=True) alchemical_potential = alchemical_state.getPotentialEnergy() delta = alchemical_potential - reference_potential print "reference system : %24.8f kcal/mol" % (reference_potential / units.kilocalories_per_mole) print "alchemically modified : %24.8f kcal/mol" % (alchemical_potential / units.kilocalories_per_mole) print "ERROR : %24.8f kcal/mol" % ((alchemical_potential - reference_potential) / units.kilocalories_per_mole) print "elapsed alchemical time %.3f s" % elapsed_time return delta
def test_annihilated_states(platform_name=None, precision=None): """Compare annihilated states in vacuum and a large box. """ from openmmtools import testsystems testsystem = testsystems.TolueneVacuum() vacuum_system = testsystem.system positions = testsystem.positions factory_args = {'ligand_atoms' : range(0,15), 'receptor_atoms' : [], 'annihilate_sterics' : False, 'annihilate_electrostatics' : True } # Create annihilated version of vacuum system. factory = AbsoluteAlchemicalFactory(vacuum_system, **factory_args) vacuum_alchemical_system = factory.createPerturbedSystem() # Make copy of system that has periodic boundaries and uses reaction field. periodic_system = copy.deepcopy(vacuum_system) box_edge = 18.5 * unit.angstroms from simtk.openmm import Vec3 periodic_system.setDefaultPeriodicBoxVectors(Vec3(box_edge,0,0), Vec3(0,box_edge,0), Vec3(0,0,box_edge)) for force in periodic_system.getForces(): if force.__class__.__name__ == 'NonbondedForce': force.setNonbondedMethod(openmm.NonbondedForce.PME) force.setCutoffDistance(9.0 * unit.angstroms) force.setUseDispersionCorrection(False) force.setReactionFieldDielectric(1.0) factory = AbsoluteAlchemicalFactory(periodic_system, **factory_args) periodic_alchemical_system = factory.createPerturbedSystem() # Compare energies platform = None if platform_name: platform = openmm.Platform.getPlatformByName(platform_name) vacuum_alchemical_1_energy = compute_energy(vacuum_alchemical_system, positions, platform=platform, precision=precision) periodic_alchemical_1_energy = compute_energy(periodic_alchemical_system, positions, platform=platform, precision=precision) #compareSystemEnergies(positions, [vacuum_alchemical_system, periodic_alchemical_system], ['vacuum (fully interacting)', 'periodic (fully interacting)'], platform=platform, precision=precision) # Set lambda = 0 lambda_value = 0.0 alchemical_state = AlchemicalState(lambda_electrostatics=lambda_value, lambda_sterics=lambda_value, lambda_torsions=lambda_value) AbsoluteAlchemicalFactory.perturbSystem(vacuum_alchemical_system, alchemical_state) AbsoluteAlchemicalFactory.perturbSystem(periodic_alchemical_system, alchemical_state) #compareSystemEnergies(positions, [vacuum_alchemical_system, periodic_alchemical_system], ['vacuum (noninteracting)', 'periodic (noninteracting)'], platform=platform, precision=precision) vacuum_alchemical_0_energy = compute_energy(vacuum_alchemical_system, positions, platform=platform, precision=precision) periodic_alchemical_0_energy = compute_energy(periodic_alchemical_system, positions, platform=platform, precision=precision) logger.info('vacuum lambda = 1 : %8.3f kcal/mol' % (vacuum_alchemical_1_energy / unit.kilocalories_per_mole)) logger.info('vacuum lambda = 0 : %8.3f kcal/mol' % (vacuum_alchemical_0_energy / unit.kilocalories_per_mole)) logger.info('difference : %8.3f kcal/mol' % ((vacuum_alchemical_1_energy - vacuum_alchemical_0_energy) / unit.kilocalories_per_mole)) logger.info('periodic lambda = 1 : %8.3f kcal/mol' % (periodic_alchemical_1_energy / unit.kilocalories_per_mole)) logger.info('periodic lambda = 0 : %8.3f kcal/mol' % (periodic_alchemical_0_energy / unit.kilocalories_per_mole)) logger.info('difference : %8.3f kcal/mol' % ((periodic_alchemical_1_energy - periodic_alchemical_0_energy) / unit.kilocalories_per_mole)) delta = (vacuum_alchemical_1_energy - vacuum_alchemical_0_energy) - (periodic_alchemical_1_energy - periodic_alchemical_0_energy) if (abs(delta) > MAX_DELTA): raise Exception("Maximum allowable difference lambda=1 energy and lambda=0 energy in vacuum and periodic box exceeded (was %.8f kcal/mol; allowed %.8f kcal/mol); test failed." % (delta / unit.kilocalories_per_mole, MAX_DELTA / unit.kilocalories_per_mole))
def __init__(self, alchemical_protocol='two-phase', nlambda=50, **kwargs): """ Create an alchemical free energy calculation SAMS test system from the provided system. Parameters ---------- alchemical_protocol : str, optional, default='two-phase' Alchemical protocol scheme to use. ['two-phase', 'fused'] nlambda : int, optional, default=50 Number of alchemical states. """ super(AlchemicalSAMSTestSystem, self).__init__(**kwargs) self.description = 'Alchemical SAMS test system' self.alchemical_protocol = alchemical_protocol if not (hasattr(self, 'topology') and hasattr(self, 'system') and hasattr(self, 'positions') and hasattr(self, 'alchemical_atoms')): raise Exception( "%s: 'topology', 'system', 'positions', and 'alchemical_atoms' properties must be defined!" % self.__class__.__name__) if not hasattr(self, 'temperature'): self.temperature = 300 * unit.kelvin if not hasattr(self, 'temperature'): self.temperature = 300 * unit.kelvin if not hasattr(self, 'pressure'): self.pressure = None # Add a MonteCarloBarostat if system does not have one has_barostat = False for force in self.system.getForces(): if force.__class__.__name__ in [ 'MonteCarloBarostat', 'MonteCarloAnisotropicBarostat' ]: has_barostat = True if (self.pressure is not None) and (not has_barostat): barostat = openmm.MonteCarloBarostat(self.pressure, self.temperature) self.system.addForce(barostat) # Create alchemically-modified system and populate thermodynamic states. from alchemy import AbsoluteAlchemicalFactory from sams import ThermodynamicState self.thermodynamic_states = list() if alchemical_protocol == 'fused': factory = AbsoluteAlchemicalFactory( self.system, ligand_atoms=self.alchemical_atoms, annihilate_electrostatics=True, annihilate_sterics=False) self.system = factory.createPerturbedSystem() from sams import ThermodynamicState alchemical_lambdas = np.linspace(1.0, 0.0, nlambda) for alchemical_lambda in alchemical_lambdas: parameters = { 'lambda_sterics': alchemical_lambda, 'lambda_electrostatics': alchemical_lambda } self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=self.temperature, pressure=self.pressure, parameters=parameters)) elif alchemical_protocol == 'two-phase': factory = AbsoluteAlchemicalFactory( self.system, ligand_atoms=self.alchemical_atoms, annihilate_electrostatics=True, annihilate_sterics=False, softcore_beta=0.0) # turn off softcore electrostatics self.system = factory.createPerturbedSystem() nelec = int(nlambda / 2.0) nvdw = nlambda - nelec for state in range(nelec + 1): parameters = { 'lambda_sterics': 1.0, 'lambda_electrostatics': (1.0 - float(state) / float(nelec)) } self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=self.temperature, pressure=self.pressure, parameters=parameters)) for state in range(1, nvdw + 1): parameters = { 'lambda_sterics': (1.0 - float(state) / float(nvdw)), 'lambda_electrostatics': 0.0 } self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=self.temperature, pressure=self.pressure, parameters=parameters)) else: raise Exception( "'alchemical_protocol' must be one of ['two-phase', 'fused']; scheme '%s' unknown." % alchemical_protocol) # Create SAMS samplers print('Setting up samplers...') from sams.samplers import SamplerState, MCMCSampler, ExpandedEnsembleSampler, SAMSSampler thermodynamic_state_index = 0 # initial thermodynamic state index thermodynamic_state = self.thermodynamic_states[ thermodynamic_state_index] sampler_state = SamplerState(positions=self.positions) self.mcmc_sampler = MCMCSampler( sampler_state=sampler_state, thermodynamic_state=thermodynamic_state, ncfile=self.ncfile) self.mcmc_sampler.timestep = 2.0 * unit.femtoseconds self.mcmc_sampler.nsteps = 500 #self.mcmc_sampler.pdbfile = open('output.pdb', 'w') self.mcmc_sampler.topology = self.topology self.mcmc_sampler.verbose = True self.exen_sampler = ExpandedEnsembleSampler(self.mcmc_sampler, self.thermodynamic_states) self.exen_sampler.verbose = True self.sams_sampler = SAMSSampler(self.exen_sampler) self.sams_sampler.verbose = True # DEBUG: Write PDB of initial frame from simtk.openmm.app import PDBFile outfile = open('initial.pdb', 'w') PDBFile.writeFile(self.topology, self.positions, outfile) outfile.close()
def test_ncmc_alchemical_integrator_stability_molecules(): """ Test NCMCAlchemicalIntegrator """ molecule_names = ['pentane', 'biphenyl', 'imatinib'] if os.environ.get("TRAVIS", None) == 'true': molecule_names = ['pentane'] for molecule_name in molecule_names: from perses.tests.utils import createSystemFromIUPAC [molecule, system, positions, topology] = createSystemFromIUPAC(molecule_name) # Eliminate half of the molecule # TODO: Use a more rigorous scheme to make sure we are really cutting the molecule in half and not just eliminating hydrogens or something. alchemical_atoms = [ index for index in range(int(system.getNumParticles() / 2)) ] # Create an alchemically-modified system. from alchemy import AbsoluteAlchemicalFactory alchemical_factory = AbsoluteAlchemicalFactory( system, ligand_atoms=alchemical_atoms, annihilate_electrostatics=True, annihilate_sterics=True) # Return the alchemically-modified system in fully-interacting form. alchemical_system = alchemical_factory.createPerturbedSystem() # Create an NCMC switching integrator. from perses.annihilation.ncmc_switching import NCMCVVAlchemicalIntegrator temperature = 300.0 * unit.kelvin functions = { 'lambda_sterics': 'lambda', 'lambda_electrostatics': 'lambda^0.5', 'lambda_torsions': 'lambda', 'lambda_angles': 'lambda^2' } ncmc_integrator = NCMCVVAlchemicalIntegrator(temperature, alchemical_system, functions, direction='delete', nsteps=10, timestep=1.0 * unit.femtoseconds) # Create a Context context = openmm.Context(alchemical_system, ncmc_integrator) context.setPositions(positions) # Run the integrator ncmc_integrator.step(1) # Check positions are finite positions = context.getState(getPositions=True).getPositions( asNumpy=True) if np.isnan(np.any(positions / positions.unit)): raise Exception('NCMCAlchemicalIntegrator gave NaN positions') if np.isnan(ncmc_integrator.getLogAcceptanceProbability(context)): raise Exception( 'NCMCAlchemicalIntegrator gave NaN logAcceptanceProbability') del context, ncmc_integrator
def benchmark(reference_system, positions, platform_name=None, nsteps=500, timestep=1.0*unit.femtoseconds, factory_args=None): """ Benchmark performance of alchemically modified system relative to original system. Parameters ---------- reference_system : simtk.openmm.System The reference System object to compare with positions : simtk.unit.Quantity with units compatible with nanometers The positions to assess energetics for. platform_name : str, optional, default=None The name of the platform to use for benchmarking. nsteps : int, optional, default=500 Number of molecular dynamics steps to use for benchmarking. timestep : simtk.unit.Quantity with units compatible with femtoseconds, optional, default=1*femtoseconds Timestep to use for benchmarking. factory_args : dict(), optional, default=None Arguments passed to AbsoluteAlchemicalFactory. """ from alchemy import AbsoluteAlchemicalFactory, AlchemicalState # Create a factory to produce alchemical intermediates. print("Creating alchemical factory...") initial_time = time.time() factory = AbsoluteAlchemicalFactory(reference_system, **factory_args) final_time = time.time() elapsed_time = final_time - initial_time print("AbsoluteAlchemicalFactory initialization took %.3f s" % elapsed_time) # Create an alchemically-perturbed state corresponding to nearly fully-interacting. # NOTE: We use a lambda slightly smaller than 1.0 because the AlchemicalFactory does not use Custom*Force softcore versions if lambda = 1.0 identically. lambda_value = 1.0 - 1.0e-6 alchemical_state = AlchemicalState(lambda_electrostatics=lambda_value, lambda_sterics=lambda_value, lambda_torsions=lambda_value) platform = None if platform_name: platform = openmm.Platform.getPlatformByName(platform_name) temperature = 300.*unit.kelvin collision_rate = 90./unit.picoseconds # Create the perturbed system. print("Creating alchemically-modified state...") initial_time = time.time() alchemical_system = factory.createPerturbedSystem(alchemical_state) final_time = time.time() elapsed_time = final_time - initial_time # Compare energies. print("Computing reference energies...") reference_integrator = openmm.LangevinIntegrator(temperature, collision_rate, timestep) if platform: reference_context = openmm.Context(reference_system, reference_integrator, platform) else: reference_context = openmm.Context(reference_system, reference_integrator) reference_context.setPositions(positions) reference_state = reference_context.getState(getEnergy=True) reference_potential = reference_state.getPotentialEnergy() print(reference_potential) print("Computing alchemical energies...") alchemical_integrator = openmm.LangevinIntegrator(temperature, collision_rate, timestep) if platform: alchemical_context = openmm.Context(alchemical_system, alchemical_integrator, platform) else: alchemical_context = openmm.Context(alchemical_system, alchemical_integrator) alchemical_context.setPositions(positions) alchemical_state = alchemical_context.getState(getEnergy=True) alchemical_potential = alchemical_state.getPotentialEnergy() print(reference_potential) # Make sure all kernels are compiled. reference_integrator.step(2) alchemical_integrator.step(2) # Time simulations. print("Simulating reference system...") initial_time = time.time() reference_integrator.step(nsteps) reference_state = reference_context.getState() #reference_state = reference_context.getState(getEnergy=True) #reference_potential = reference_state.getPotentialEnergy() final_time = time.time() reference_time = final_time - initial_time print("Simulating alchemical system...") initial_time = time.time() alchemical_integrator.step(nsteps) reference_state = alchemical_context.getState() #alchemical_state = alchemical_context.getState(getEnergy=True) #alchemical_potential = alchemical_state.getPotentialEnergy() final_time = time.time() alchemical_time = final_time - initial_time seconds_per_day = (1.*unit.day)/(1.*unit.seconds) print("TIMINGS") print("reference system : %12.3f s for %8d steps (%12.3f ms/step; %12.3f ns/day)" % (reference_time, nsteps, reference_time/nsteps*1000, nsteps*timestep*(seconds_per_day/reference_time)/unit.nanoseconds)) print("alchemical system : %12.3f s for %8d steps (%12.3f ms/step; %12.3f ns/day)" % (alchemical_time, nsteps, alchemical_time/nsteps*1000, nsteps*timestep*(seconds_per_day/alchemical_time)/unit.nanoseconds)) print("alchemical simulation is %12.3f x slower than unperturbed system" % (alchemical_time / reference_time))
def benchmark( reference_system, coordinates, receptor_atoms, ligand_atoms, platform_name="CUDA", annihilateElectrostatics=True, annihilateLennardJones=False, nsteps=500, ): """ Benchmark performance relative to unmodified system. ARGUMENTS reference_system (simtk.openmm.System) - the reference System object to compare with coordinates - the coordinates to assess energetics for receptor_atoms (list of int) - the list of receptor atoms ligand_atoms (list of int) - the list of ligand atoms to alchemically modify """ import simtk.unit as units import simtk.openmm as openmm import time # Create a factory to produce alchemical intermediates. print "Creating alchemical factory..." initial_time = time.time() factory = AbsoluteAlchemicalFactory(reference_system, ligand_atoms=ligand_atoms) final_time = time.time() elapsed_time = final_time - initial_time print "AbsoluteAlchemicalFactory initialization took %.3f s" % elapsed_time # Create an alchemically-perturbed state corresponding to nearly fully-interacting. # NOTE: We use a lambda slightly smaller than 1.0 because the AlchemicalFactory does not use Custom*Force softcore versions if lambda = 1.0 identically. lambda_value = 1.0 - 1.0e-6 alchemical_state = AlchemicalState(0.00, lambda_value, lambda_value, lambda_value) alchemical_state.annihilateElectrostatics = annihilateElectrostatics alchemical_state.annihilateLennardJones = annihilateLennardJones platform = openmm.Platform.getPlatformByName(platform_name) # Create the perturbed system. print "Creating alchemically-modified state..." initial_time = time.time() alchemical_system = factory.createPerturbedSystem(alchemical_state) final_time = time.time() elapsed_time = final_time - initial_time # Compare energies. timestep = 1.0 * units.femtosecond print "Computing reference energies..." reference_integrator = openmm.VerletIntegrator(timestep) reference_context = openmm.Context(reference_system, reference_integrator, platform) reference_context.setPositions(coordinates) reference_state = reference_context.getState(getEnergy=True) reference_potential = reference_state.getPotentialEnergy() print "Computing alchemical energies..." alchemical_integrator = openmm.VerletIntegrator(timestep) alchemical_context = openmm.Context(alchemical_system, alchemical_integrator, platform) alchemical_context.setPositions(coordinates) alchemical_state = alchemical_context.getState(getEnergy=True) alchemical_potential = alchemical_state.getPotentialEnergy() delta = alchemical_potential - reference_potential # Make sure all kernels are compiled. reference_integrator.step(1) alchemical_integrator.step(1) # Time simulations. import time print "Simulating reference system..." initial_time = time.time() reference_integrator.step(nsteps) reference_state = reference_context.getState(getEnergy=True) reference_potential = reference_state.getPotentialEnergy() final_time = time.time() reference_time = final_time - initial_time print "Simulating alchemical system..." initial_time = time.time() alchemical_integrator.step(nsteps) alchemical_state = alchemical_context.getState(getEnergy=True) alchemical_potential = alchemical_state.getPotentialEnergy() final_time = time.time() alchemical_time = final_time - initial_time print "TIMINGS" print "reference system : %12.3f s for %8d steps (%12.3f ms/step)" % ( reference_time, nsteps, reference_time / nsteps * 1000, ) print "alchemical system : %12.3f s for %8d steps (%12.3f ms/step)" % ( alchemical_time, nsteps, alchemical_time / nsteps * 1000, ) print "alchemical simulation is %12.3f x slower than unperturbed system" % (alchemical_time / reference_time) return delta
def overlap_check( reference_system, positions, receptor_atoms, ligand_atoms, platform_name=None, annihilate_electrostatics=True, annihilate_sterics=False, precision=None, nsteps=50, nsamples=200, ): """ Test overlap between reference system and alchemical system by running a short simulation. Parameters ---------- reference_system : simtk.openmm.System The reference System object to compare with positions : simtk.unit.Quantity with units compatible with nanometers The positions to assess energetics for. receptor_atoms : list of int The list of receptor atoms. ligand_atoms : list of int The list of ligand atoms to alchemically modify. platform_name : str, optional, default=None The name of the platform to use for benchmarking. annihilate_electrostatics : bool, optional, default=True If True, electrostatics will be annihilated; if False, decoupled. annihilate_sterics : bool, optional, default=False If True, sterics will be annihilated; if False, decoupled. nsteps : int, optional, default=50 Number of molecular dynamics steps between samples. nsamples : int, optional, default=100 Number of samples to collect. """ # Create a fully-interacting alchemical state. factory = AbsoluteAlchemicalFactory(reference_system, ligand_atoms=ligand_atoms) alchemical_state = AlchemicalState() alchemical_system = factory.createPerturbedSystem(alchemical_state) temperature = 300.0 * unit.kelvin collision_rate = 5.0 / unit.picoseconds timestep = 2.0 * unit.femtoseconds kT = kB * temperature # Select platform. platform = None if platform_name: platform = openmm.Platform.getPlatformByName(platform_name) # Create integrators. reference_integrator = openmm.LangevinIntegrator(temperature, collision_rate, timestep) alchemical_integrator = openmm.VerletIntegrator(timestep) # Create contexts. if platform: reference_context = openmm.Context(reference_system, reference_integrator, platform) alchemical_context = openmm.Context(alchemical_system, alchemical_integrator, platform) else: reference_context = openmm.Context(reference_system, reference_integrator) alchemical_context = openmm.Context(alchemical_system, alchemical_integrator) # Collect simulation data. reference_context.setPositions(positions) du_n = np.zeros([nsamples], np.float64) # du_n[n] is the for sample in range(nsamples): # Run dynamics. reference_integrator.step(nsteps) # Get reference energies. reference_state = reference_context.getState(getEnergy=True, getPositions=True) reference_potential = reference_state.getPotentialEnergy() # Get alchemical energies. alchemical_context.setPositions(reference_state.getPositions()) alchemical_state = alchemical_context.getState(getEnergy=True) alchemical_potential = alchemical_state.getPotentialEnergy() du_n[sample] = (alchemical_potential - reference_potential) / kT # Clean up. del reference_context, alchemical_context # Discard data to equilibration and subsample. from pymbar import timeseries [t0, g, Neff] = timeseries.detectEquilibration(du_n) indices = timeseries.subsampleCorrelatedData(du_n, g=g) du_n = du_n[indices] # Compute statistics. from pymbar import EXP [DeltaF, dDeltaF] = EXP(du_n) # Raise an exception if the error is larger than 3kT. MAX_DEVIATION = 3.0 # kT if dDeltaF > MAX_DEVIATION: report = "DeltaF = %12.3f +- %12.3f kT (%5d samples, g = %6.1f)" % (DeltaF, dDeltaF, Neff, g) raise Exception(report) return
def lambda_trace(reference_system, positions, platform_name=None, precision=None, nsteps=100, factory_args=None): """ Compute potential energy as a function of lambda. """ # Create a factory to produce alchemical intermediates. factory = AbsoluteAlchemicalFactory(reference_system, **factory_args) platform = None if platform_name: # Get platform. platform = openmm.Platform.getPlatformByName(platform_name) if precision: if platform_name == 'CUDA': platform.setDefaultPropertyValue('CudaPrecision', precision) elif platform_name == 'OpenCL': platform.setDefaultPropertyValue('OpenCLPrecision', precision) # Take equally-sized steps. delta = 1.0 / nsteps def compute_potential(system, positions, platform=None): timestep = 1.0 * unit.femtoseconds integrator = openmm.VerletIntegrator(timestep) if platform: context = openmm.Context(system, integrator, platform) else: context = openmm.Context(system, integrator) context.setPositions(positions) state = context.getState(getEnergy=True) potential = state.getPotentialEnergy() del integrator, context return potential # Compute unmodified energy. u_original = compute_potential(reference_system, positions, platform) # Scan through lambda values. lambda_i = np.zeros([nsteps+1], np.float64) # lambda values for u_i u_i = unit.Quantity(np.zeros([nsteps+1], np.float64), unit.kilocalories_per_mole) # u_i[i] is the potential energy for lambda_i[i] for i in range(nsteps+1): lambda_value = 1.0-i*delta # compute lambda value for this step alchemical_system = factory.createPerturbedSystem(AlchemicalState(lambda_electrostatics=lambda_value, lambda_sterics=lambda_value, lambda_torsions=lambda_value)) lambda_i[i] = lambda_value u_i[i] = compute_potential(alchemical_system, positions, platform) logger.info("%12.9f %24.8f kcal/mol" % (lambda_i[i], u_i[i] / unit.kilocalories_per_mole)) # Write figure as PDF. import pylab from matplotlib.backends.backend_pdf import PdfPages import matplotlib.pyplot as plt with PdfPages('lambda-trace.pdf') as pdf: fig = plt.figure(figsize=(10, 5)) ax = fig.add_subplot(111) plt.plot(1, u_original / unit.kilocalories_per_mole, 'ro', label='unmodified') plt.plot(lambda_i, u_i / unit.kilocalories_per_mole, 'k.', label='alchemical') plt.title('T4 lysozyme L99A + p-xylene : AMBER96 + OBC GBSA') plt.ylabel('potential (kcal/mol)') plt.xlabel('lambda') ax.legend() rstyle(ax) pdf.savefig() # saves the current figure into a pdf page plt.close() return
def overlap_check(reference_system, positions, platform_name=None, precision=None, nsteps=50, nsamples=200, factory_args=None, cached_trajectory_filename=None): """ Test overlap between reference system and alchemical system by running a short simulation. Parameters ---------- reference_system : simtk.openmm.System The reference System object to compare with positions : simtk.unit.Quantity with units compatible with nanometers The positions to assess energetics for. platform_name : str, optional, default=None The name of the platform to use for benchmarking. nsteps : int, optional, default=50 Number of molecular dynamics steps between samples. nsamples : int, optional, default=100 Number of samples to collect. factory_args : dict(), optional, default=None Arguments passed to AbsoluteAlchemicalFactory. cached_trajectory_filename : str, optional, default=None If specified, attempt to cache (or reuse) trajectory. """ # Create a fully-interacting alchemical state. factory = AbsoluteAlchemicalFactory(reference_system, **factory_args) alchemical_state = AlchemicalState() alchemical_system = factory.createPerturbedSystem(alchemical_state) temperature = 300.0 * unit.kelvin collision_rate = 5.0 / unit.picoseconds timestep = 2.0 * unit.femtoseconds kT = (kB * temperature) # Select platform. platform = None if platform_name: platform = openmm.Platform.getPlatformByName(platform_name) # Create integrators. reference_integrator = openmm.LangevinIntegrator(temperature, collision_rate, timestep) alchemical_integrator = openmm.VerletIntegrator(timestep) # Create contexts. if platform: reference_context = openmm.Context(reference_system, reference_integrator, platform) alchemical_context = openmm.Context(alchemical_system, alchemical_integrator, platform) else: reference_context = openmm.Context(reference_system, reference_integrator) alchemical_context = openmm.Context(alchemical_system, alchemical_integrator) ncfile = None if cached_trajectory_filename: cache_mode = 'write' # Try reading from cache from netCDF4 import Dataset if os.path.exists(cached_trajectory_filename): try: ncfile = Dataset(cached_trajectory_filename, 'r') if (ncfile.variables['positions'].shape == (nsamples, reference_system.getNumParticles(), 3)): # Read the cache if everything matches cache_mode = 'read' except: pass if cache_mode == 'write': # If anything went wrong, create a new cache. try: (pathname, filename) = os.path.split(cached_trajectory_filename) if not os.path.exists(pathname): os.makedirs(pathname) ncfile = Dataset(cached_trajectory_filename, 'w', format='NETCDF4') ncfile.createDimension('samples', 0) ncfile.createDimension('atoms', reference_system.getNumParticles()) ncfile.createDimension('spatial', 3) ncfile.createVariable('positions', 'f4', ('samples', 'atoms', 'spatial')) except Exception as e: logger.info(str(e)) logger.info('Could not create a trajectory cache (%s).' % cached_trajectory_filename) ncfile = None # Collect simulation data. reference_context.setPositions(positions) du_n = np.zeros([nsamples], np.float64) # du_n[n] is the print() import click with click.progressbar(range(nsamples)) as bar: for sample in bar: if cached_trajectory_filename and (cache_mode == 'read'): # Load cached frames. positions = unit.Quantity(ncfile.variables['positions'][sample,:,:], unit.nanometers) reference_context.setPositions(positions) else: # Run dynamics. reference_integrator.step(nsteps) # Get reference energies. reference_state = reference_context.getState(getEnergy=True, getPositions=True) reference_potential = reference_state.getPotentialEnergy() if np.isnan(reference_potential/kT): raise Exception("Reference potential is NaN") # Get alchemical energies. alchemical_context.setPositions(reference_state.getPositions(asNumpy=True)) alchemical_state = alchemical_context.getState(getEnergy=True) alchemical_potential = alchemical_state.getPotentialEnergy() if np.isnan(alchemical_potential/kT): raise Exception("Alchemical potential is NaN") du_n[sample] = (alchemical_potential - reference_potential) / kT if cached_trajectory_filename and (cache_mode == 'write') and (ncfile is not None): ncfile.variables['positions'][sample,:,:] = reference_state.getPositions(asNumpy=True) / unit.nanometers # Clean up. del reference_context, alchemical_context if cached_trajectory_filename and (ncfile is not None): ncfile.close() # Discard data to equilibration and subsample. from pymbar import timeseries [t0, g, Neff] = timeseries.detectEquilibration(du_n) indices = timeseries.subsampleCorrelatedData(du_n, g=g) du_n = du_n[indices] # Compute statistics. from pymbar import EXP [DeltaF, dDeltaF] = EXP(du_n) # Raise an exception if the error is larger than 3kT. MAX_DEVIATION = 3.0 # kT if (dDeltaF > MAX_DEVIATION): report = "DeltaF = %12.3f +- %12.3f kT (%5d samples, g = %6.1f)" % (DeltaF, dDeltaF, Neff, g) raise Exception(report) return
def __init__(self, **kwargs): super(LoopSoftening, self).__init__(**kwargs) self.description = 'Alchemical Loop Softening script' padding = 9.0*unit.angstrom explicit_solvent_model = 'tip3p' setup_path = 'data/mtor' # Create topology, positions, and system. from pkg_resources import resource_filename gaff_xml_filename = resource_filename('sams', 'data/gaff.xml') system_generators = dict() ffxmls = [gaff_xml_filename, 'amber99sbildn.xml', 'tip3p.xml'] forcefield_kwargs={ 'nonbondedMethod' : app.CutoffPeriodic, 'nonbondedCutoff' : 9.0 * unit.angstrom, 'implicitSolvent' : None, 'constraints' : app.HBonds, 'rigidWater' : True } # Load topologies and positions for all components print('Creating mTOR test system...') forcefield = app.ForceField(*ffxmls) from simtk.openmm.app import PDBFile, Modeller pdb_filename = resource_filename('sams', os.path.join(setup_path, 'mtor_pdbfixer_apo.pdb')) pdbfile = PDBFile(pdb_filename) modeller = app.Modeller(pdbfile.topology, pdbfile.positions) print('Adding solvent...') modeller.addSolvent(forcefield, model=explicit_solvent_model, padding=padding) self.topology = modeller.getTopology() self.positions = modeller.getPositions() print('Creating system...') self.system = forcefield.createSystem(self.topology, **forcefield_kwargs) # DEBUG: Write PDB outfile = open('initial.pdb', 'w') PDBFile.writeFile(self.topology, self.positions, outfile) outfile.close() # Atom Selection using MDtraj res_pairs = [[403, 483], [1052, 1109]] t = md.load(pdb_filename) alchemical_atoms = [] for x in res_pairs: start = min(t.top.select('residue %s' % min(x))) end = max(t.top.select('residue %s' % max(x))) + 1 alchemical_atoms.append(list(range(start, end))) #print(alchemical_atoms) # Add a MonteCarloBarostat #temperature = 300 * unit.kelvin # will be replaced as thermodynamic state is updated #pressure = 1.0 * unit.atmospheres #barostat = openmm.MonteCarloBarostat(pressure, temperature) #self.system.addForce(barostat) # Create thermodynamic states. print('Creating alchemically-modified system...') temperature = 300 * unit.kelvin pressure = 1.0 * unit.atmospheres alchemical_atoms = range(0,69) # Abl:imatinib from alchemy import AbsoluteAlchemicalFactory factory = AbsoluteAlchemicalFactory(self.system, ligand_atoms=alchemical_atoms, annihilate_electrostatics=True, annihilate_sterics=False, softcore_beta=0.0) # turn off softcore electrostatics self.system = factory.createPerturbedSystem() print('Setting up alchemical intermediates...') from sams import ThermodynamicState self.thermodynamic_states = list() for state in range(251): parameters = {'lambda_sterics' : 1.0, 'lambda_electrostatics' : (1.0 - float(state)/250.0) } self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=temperature, parameters=parameters) ) for state in range(1,251): parameters = {'lambda_sterics' : (1.0 - float(state)/250.0), 'lambda_electrostatics' : 0.0 } self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=temperature, parameters=parameters) ) # Create SAMS samplers print('Setting up samplers...') from sams.samplers import SamplerState, MCMCSampler, ExpandedEnsembleSampler, SAMSSampler thermodynamic_state_index = 0 # initial thermodynamic state index thermodynamic_state = self.thermodynamic_states[thermodynamic_state_index] sampler_state = SamplerState(positions=self.positions) self.mcmc_sampler = MCMCSampler(sampler_state=sampler_state, thermodynamic_state=thermodynamic_state, ncfile=self.ncfile) self.mcmc_sampler.pdbfile = open('output.pdb', 'w') self.mcmc_sampler.topology = self.topology self.mcmc_sampler.verbose = True self.exen_sampler = ExpandedEnsembleSampler(self.mcmc_sampler, self.thermodynamic_states) self.exen_sampler.verbose = True self.sams_sampler = SAMSSampler(self.exen_sampler) self.sams_sampler.verbose = True
def lambda_trace( reference_system, coordinates, receptor_atoms, ligand_atoms, platform_name="CUDA", annihilateElectrostatics=True, annihilateLennardJones=False, nsteps=50, ): """ Compute potential energy as a function of lambda. """ # Create a factory to produce alchemical intermediates. factory = AbsoluteAlchemicalFactory(reference_system, ligand_atoms=ligand_atoms) # Get platform. platform = openmm.Platform.getPlatformByName(platform_name) delta = 1.0 / nsteps def compute_potential(system, coordinates, platform): timestep = 1.0 * units.femtoseconds integrator = openmm.VerletIntegrator(timestep) context = openmm.Context(system, integrator, platform) context.setPositions(coordinates) state = context.getState(getEnergy=True) potential = state.getPotentialEnergy() del integrator, context return potential # discharging outfile = open("discharging-trace.out", "w") for i in range(nsteps + 1): lambda_value = 1.0 - i * delta alchemical_system = factory.createPerturbedSystem( AlchemicalState( 0, lambda_value, 1, 1, annihilateElectrostatics=annihilateElectrostatics, annihilateLennardJones=annihilateLennardJones, ) ) potential = compute_potential(alchemical_system, coordinates, platform) line = "%12.6f %24.6f" % (lambda_value, potential / units.kilocalories_per_mole) outfile.write(line + "\n") print line outfile.close() # decoupling outfile = open("decoupling-trace.out", "w") for i in range(nsteps + 1): lambda_value = 1.0 - i * delta alchemical_system = factory.createPerturbedSystem( AlchemicalState( 0, 0, lambda_value, 1, annihilateElectrostatics=annihilateElectrostatics, annihilateLennardJones=annihilateLennardJones, ) ) potential = compute_potential(alchemical_system, coordinates, platform) line = "%12.6f %24.6f" % (lambda_value, potential / units.kilocalories_per_mole) outfile.write(line + "\n") print line outfile.close() return
def benchmark( reference_system, positions, receptor_atoms, ligand_atoms, platform_name=None, annihilate_electrostatics=True, annihilate_sterics=False, nsteps=500, timestep=1.0 * unit.femtoseconds, ): """ Benchmark performance of alchemically modified system relative to original system. Parameters ---------- reference_system : simtk.openmm.System The reference System object to compare with positions : simtk.unit.Quantity with units compatible with nanometers The positions to assess energetics for. receptor_atoms : list of int The list of receptor atoms. ligand_atoms : list of int The list of ligand atoms to alchemically modify. platform_name : str, optional, default=None The name of the platform to use for benchmarking. annihilate_electrostatics : bool, optional, default=True If True, electrostatics will be annihilated; if False, decoupled. annihilate_sterics : bool, optional, default=False If True, sterics will be annihilated; if False, decoupled. nsteps : int, optional, default=500 Number of molecular dynamics steps to use for benchmarking. timestep : simtk.unit.Quantity with units compatible with femtoseconds, optional, default=1*femtoseconds Timestep to use for benchmarking. """ # Create a factory to produce alchemical intermediates. logger.info("Creating alchemical factory...") initial_time = time.time() factory = AbsoluteAlchemicalFactory( reference_system, ligand_atoms=ligand_atoms, annihilate_electrostatics=annihilate_electrostatics, annihilate_sterics=annihilate_sterics, ) final_time = time.time() elapsed_time = final_time - initial_time logger.info("AbsoluteAlchemicalFactory initialization took %.3f s" % elapsed_time) # Create an alchemically-perturbed state corresponding to nearly fully-interacting. # NOTE: We use a lambda slightly smaller than 1.0 because the AlchemicalFactory does not use Custom*Force softcore versions if lambda = 1.0 identically. lambda_value = 1.0 - 1.0e-6 alchemical_state = AlchemicalState( lambda_coulomb=lambda_value, lambda_sterics=lambda_value, lambda_torsions=lambda_value ) platform = None if platform_name: platform = openmm.Platform.getPlatformByName(platform_name) # Create the perturbed system. logger.info("Creating alchemically-modified state...") initial_time = time.time() alchemical_system = factory.createPerturbedSystem(alchemical_state) final_time = time.time() elapsed_time = final_time - initial_time # Compare energies. logger.info("Computing reference energies...") reference_integrator = openmm.VerletIntegrator(timestep) if platform: reference_context = openmm.Context(reference_system, reference_integrator, platform) else: reference_context = openmm.Context(reference_system, reference_integrator) reference_context.setPositions(positions) reference_state = reference_context.getState(getEnergy=True) reference_potential = reference_state.getPotentialEnergy() logger.info("Computing alchemical energies...") alchemical_integrator = openmm.VerletIntegrator(timestep) if platform: alchemical_context = openmm.Context(alchemical_system, alchemical_integrator, platform) else: alchemical_context = openmm.Context(alchemical_system, alchemical_integrator) alchemical_context.setPositions(positions) alchemical_state = alchemical_context.getState(getEnergy=True) alchemical_potential = alchemical_state.getPotentialEnergy() delta = alchemical_potential - reference_potential # Make sure all kernels are compiled. reference_integrator.step(1) alchemical_integrator.step(1) # Time simulations. logger.info("Simulating reference system...") initial_time = time.time() reference_integrator.step(nsteps) reference_state = reference_context.getState(getEnergy=True) reference_potential = reference_state.getPotentialEnergy() final_time = time.time() reference_time = final_time - initial_time logger.info("Simulating alchemical system...") initial_time = time.time() alchemical_integrator.step(nsteps) alchemical_state = alchemical_context.getState(getEnergy=True) alchemical_potential = alchemical_state.getPotentialEnergy() final_time = time.time() alchemical_time = final_time - initial_time logger.info("TIMINGS") logger.info( "reference system : %12.3f s for %8d steps (%12.3f ms/step)" % (reference_time, nsteps, reference_time / nsteps * 1000) ) logger.info( "alchemical system : %12.3f s for %8d steps (%12.3f ms/step)" % (alchemical_time, nsteps, alchemical_time / nsteps * 1000) ) logger.info("alchemical simulation is %12.3f x slower than unperturbed system" % (alchemical_time / reference_time)) return delta
def benchmark(reference_system, positions, platform_name=None, nsteps=500, timestep=1.0 * unit.femtoseconds, factory_args=None): """ Benchmark performance of alchemically modified system relative to original system. Parameters ---------- reference_system : simtk.openmm.System The reference System object to compare with positions : simtk.unit.Quantity with units compatible with nanometers The positions to assess energetics for. platform_name : str, optional, default=None The name of the platform to use for benchmarking. nsteps : int, optional, default=500 Number of molecular dynamics steps to use for benchmarking. timestep : simtk.unit.Quantity with units compatible with femtoseconds, optional, default=1*femtoseconds Timestep to use for benchmarking. factory_args : dict(), optional, default=None Arguments passed to AbsoluteAlchemicalFactory. """ from alchemy import AbsoluteAlchemicalFactory, AlchemicalState # Create a factory to produce alchemical intermediates. print("Creating alchemical factory...") initial_time = time.time() factory = AbsoluteAlchemicalFactory(reference_system, **factory_args) final_time = time.time() elapsed_time = final_time - initial_time print("AbsoluteAlchemicalFactory initialization took %.3f s" % elapsed_time) # Create an alchemically-perturbed state corresponding to nearly fully-interacting. # NOTE: We use a lambda slightly smaller than 1.0 because the AlchemicalFactory does not use Custom*Force softcore versions if lambda = 1.0 identically. lambda_value = 1.0 - 1.0e-6 alchemical_state = AlchemicalState(lambda_electrostatics=lambda_value, lambda_sterics=lambda_value, lambda_torsions=lambda_value) platform = None if platform_name: platform = openmm.Platform.getPlatformByName(platform_name) temperature = 300. * unit.kelvin collision_rate = 90. / unit.picoseconds # Create the perturbed system. print("Creating alchemically-modified state...") initial_time = time.time() alchemical_system = factory.createPerturbedSystem(alchemical_state) final_time = time.time() elapsed_time = final_time - initial_time # Compare energies. print("Computing reference energies...") reference_integrator = openmm.LangevinIntegrator(temperature, collision_rate, timestep) if platform: reference_context = openmm.Context(reference_system, reference_integrator, platform) else: reference_context = openmm.Context(reference_system, reference_integrator) reference_context.setPositions(positions) reference_state = reference_context.getState(getEnergy=True) reference_potential = reference_state.getPotentialEnergy() print(reference_potential) print("Computing alchemical energies...") alchemical_integrator = openmm.LangevinIntegrator(temperature, collision_rate, timestep) if platform: alchemical_context = openmm.Context(alchemical_system, alchemical_integrator, platform) else: alchemical_context = openmm.Context(alchemical_system, alchemical_integrator) alchemical_context.setPositions(positions) alchemical_state = alchemical_context.getState(getEnergy=True) alchemical_potential = alchemical_state.getPotentialEnergy() print(reference_potential) # Make sure all kernels are compiled. reference_integrator.step(2) alchemical_integrator.step(2) # Time simulations. print("Simulating reference system...") initial_time = time.time() reference_integrator.step(nsteps) reference_state = reference_context.getState() #reference_state = reference_context.getState(getEnergy=True) #reference_potential = reference_state.getPotentialEnergy() final_time = time.time() reference_time = final_time - initial_time print("Simulating alchemical system...") initial_time = time.time() alchemical_integrator.step(nsteps) reference_state = alchemical_context.getState() #alchemical_state = alchemical_context.getState(getEnergy=True) #alchemical_potential = alchemical_state.getPotentialEnergy() final_time = time.time() alchemical_time = final_time - initial_time seconds_per_day = (1. * unit.day) / (1. * unit.seconds) print("TIMINGS") print( "reference system : %12.3f s for %8d steps (%12.3f ms/step; %12.3f ns/day)" % (reference_time, nsteps, reference_time / nsteps * 1000, nsteps * timestep * (seconds_per_day / reference_time) / unit.nanoseconds)) print( "alchemical system : %12.3f s for %8d steps (%12.3f ms/step; %12.3f ns/day)" % (alchemical_time, nsteps, alchemical_time / nsteps * 1000, nsteps * timestep * (seconds_per_day / alchemical_time) / unit.nanoseconds)) print("alchemical simulation is %12.3f x slower than unperturbed system" % (alchemical_time / reference_time))
def testAlchemicalFactory( reference_system, coordinates, receptor_atoms, ligand_atoms, platform_name="CUDA", annihilateElectrostatics=True, annihilateLennardJones=False, ): """ Compare energies of reference system and fully-interacting alchemically modified system. ARGUMENTS reference_system (simtk.openmm.System) - the reference System object to compare with coordinates - the coordinates to assess energetics for receptor_atoms (list of int) - the list of receptor atoms ligand_atoms (list of int) - the list of ligand atoms to alchemically modify """ import simtk.unit as units import simtk.openmm as openmm import time # Create a factory to produce alchemical intermediates. print "Creating alchemical factory..." initial_time = time.time() factory = AbsoluteAlchemicalFactory(reference_system, ligand_atoms=ligand_atoms) final_time = time.time() elapsed_time = final_time - initial_time print "AbsoluteAlchemicalFactory initialization took %.3f s" % elapsed_time platform = openmm.Platform.getPlatformByName(platform_name) delta = 0.001 delta = 1.0e-6 compareSystemEnergies( coordinates, [ reference_system, factory.createPerturbedSystem( AlchemicalState( 0, 1 - delta, 1, 1, annihilateElectrostatics=annihilateElectrostatics, annihilateLennardJones=annihilateLennardJones, ) ), ], ["reference", "partially discharged"], platform=platform, ) compareSystemEnergies( coordinates, [ factory.createPerturbedSystem( AlchemicalState( 0, delta, 1, 1, annihilateElectrostatics=annihilateElectrostatics, annihilateLennardJones=annihilateLennardJones, ) ), factory.createPerturbedSystem( AlchemicalState( 0, 0.0, 1, 1, annihilateElectrostatics=annihilateElectrostatics, annihilateLennardJones=annihilateLennardJones, ) ), ], ["partially charged", "discharged"], platform=platform, ) compareSystemEnergies( coordinates, [ factory.createPerturbedSystem( AlchemicalState( 0, 0, 1, 1, annihilateElectrostatics=annihilateElectrostatics, annihilateLennardJones=annihilateLennardJones, ) ), factory.createPerturbedSystem( AlchemicalState( 0, 0, 1 - delta, 1, annihilateElectrostatics=annihilateElectrostatics, annihilateLennardJones=annihilateLennardJones, ) ), ], ["discharged", "partially decoupled"], platform=platform, ) compareSystemEnergies( coordinates, [ factory.createPerturbedSystem( AlchemicalState( 0, 0, delta, 1, annihilateElectrostatics=annihilateElectrostatics, annihilateLennardJones=annihilateLennardJones, ) ), factory.createPerturbedSystem( AlchemicalState( 0, 0, 0, 1, annihilateElectrostatics=annihilateElectrostatics, annihilateLennardJones=annihilateLennardJones, ) ), ], ["partially coupled", "decoupled"], platform=platform, ) return
""" Create alchemical intermediates for default alchemical protocol for one water in a water box. """ from alchemy import AbsoluteAlchemicalFactory, AlchemicalState from openmmtools import testsystems # Create a reference system. print "Creating a water box..." waterbox = testsystems.WaterBox() [reference_system, positions] = [waterbox.system, waterbox.positions] # Create a factory to produce alchemical intermediates. print "Creating an alchemical factory..." factory = AbsoluteAlchemicalFactory(reference_system, ligand_atoms=[0, 1, 2]) # Create a perturbed systems using this protocol. print "Creating a perturbed system..." alchemical_state = AlchemicalState() alchemical_system = factory.createPerturbedSystem(alchemical_state) # Perturb this system. print "Perturbing the system..." alchemical_state = AlchemicalState(lambda_sterics=0.90, lambda_electrostatics=0.90) factory.perturbSystem(alchemical_system, alchemical_state)
reference_system.addForce(barostat) # Identify ligand indices by residue name print('Identifying ligand atoms to be alchemically modified...') reference = md.load(pdb_filename) alchemical_atoms = reference.topology.select(ligand_dsl_selection) # these atoms will be alchemically softened alchemical_atoms = [ int(index) for index in alchemical_atoms ] # recode as Python int print("MDTraj DSL selection '%s' identified %d atoms" % (ligand_dsl_selection, len(alchemical_atoms))) # Create alchemically-modified system using fused softcore electrostatics and sterics print('Creating alchemically modified system...') print('lambda schedule: %s' % str(alchemical_lambdas)) from alchemy import AbsoluteAlchemicalFactory from sams import ThermodynamicState factory = AbsoluteAlchemicalFactory(reference_system, ligand_atoms=alchemical_atoms, annihilate_electrostatics=True, annihilate_sterics=False) system = factory.createPerturbedSystem() # Add umbrella restraint with global variable to control umbrella position print('umbrella schedule between atoms %d and %d: %s' % (umbrella_atoms[0], umbrella_atoms[1], str(umbrella_distances))) energy_function = '(umbrella_K/2.0)*(r-umbrella_r0)^2' umbrella_force = openmm.CustomBondForce(energy_function) umbrella_force.addGlobalParameter('umbrella_K', 0.0) # spring constant umbrella_force.addGlobalParameter('umbrella_r0', 0.0) # umbrella distance umbrella_force.addBond(umbrella_atoms[0], umbrella_atoms[1], []) umbrella_K = kT/umbrella_sigma**2 system.addForce(umbrella_force) # Create thermodynamic states thermodynamic_states = list() for alchemical_lambda in alchemical_lambdas: # Umbrella off state