def generate_vacuum_hybrid_topology(mol_name="naphthalene", ref_mol_name="benzene"): from topology_proposal import SmallMoleculeSetProposalEngine, TopologyProposal import simtk.openmm.app as app from openmoltools import forcefield_generators from perses.tests.utils import createOEMolFromIUPAC, createSystemFromIUPAC, get_data_filename m, unsolv_old_system, pos_old, top_old = createSystemFromIUPAC(mol_name) refmol = createOEMolFromIUPAC(ref_mol_name) initial_smiles = oechem.OEMolToSmiles(m) final_smiles = oechem.OEMolToSmiles(refmol) gaff_xml_filename = get_data_filename("data/gaff.xml") forcefield = app.ForceField(gaff_xml_filename, 'tip3p.xml') forcefield.registerTemplateGenerator(forcefield_generators.gaffTemplateGenerator) solvated_system = forcefield.createSystem(top_old) gaff_filename = get_data_filename('data/gaff.xml') system_generator = SystemGenerator([gaff_filename, 'amber99sbildn.xml', 'tip3p.xml']) geometry_engine = FFAllAngleGeometryEngine() proposal_engine = SmallMoleculeSetProposalEngine( [initial_smiles, final_smiles], system_generator, residue_name=mol_name) #generate topology proposal topology_proposal = proposal_engine.propose(solvated_system, top_old) #generate new positions with geometry engine new_positions, _ = geometry_engine.propose(topology_proposal, pos_old, beta) return topology_proposal, pos_old, new_positions
def generate_solvated_hybrid_test_topology(current_mol_name="naphthalene", proposed_mol_name="benzene"): """ Generate a test solvated topology proposal, current positions, and new positions triplet from two IUPAC molecule names. Parameters ---------- current_mol_name : str, optional name of the first molecule proposed_mol_name : str, optional name of the second molecule Returns ------- topology_proposal : perses.rjmc.topology_proposal The topology proposal representing the transformation current_positions : np.array, unit-bearing The positions of the initial system new_positions : np.array, unit-bearing The positions of the new system """ import simtk.openmm.app as app from openmoltools import forcefield_generators from perses.tests.utils import createOEMolFromIUPAC, createSystemFromIUPAC, get_data_filename current_mol, unsolv_old_system, pos_old, top_old = createSystemFromIUPAC(current_mol_name) proposed_mol = createOEMolFromIUPAC(proposed_mol_name) initial_smiles = oechem.OEMolToSmiles(current_mol) final_smiles = oechem.OEMolToSmiles(proposed_mol) gaff_xml_filename = get_data_filename("data/gaff.xml") forcefield = app.ForceField(gaff_xml_filename, 'tip3p.xml') forcefield.registerTemplateGenerator(forcefield_generators.gaffTemplateGenerator) modeller = app.Modeller(top_old, pos_old) modeller.addSolvent(forcefield, model='tip3p', padding=9.0*unit.angstrom) solvated_topology = modeller.getTopology() solvated_positions = modeller.getPositions() solvated_system = forcefield.createSystem(solvated_topology, nonbondedMethod=app.PME, removeCMMotion=False) barostat = openmm.MonteCarloBarostat(1.0*unit.atmosphere, temperature, 50) solvated_system.addForce(barostat) gaff_filename = get_data_filename('data/gaff.xml') system_generator = SystemGenerator([gaff_filename, 'amber99sbildn.xml', 'tip3p.xml'], barostat=barostat, forcefield_kwargs={'removeCMMotion': False, 'nonbondedMethod': app.PME}) geometry_engine = geometry.FFAllAngleGeometryEngine() proposal_engine = SmallMoleculeSetProposalEngine( [initial_smiles, final_smiles], system_generator, residue_name=current_mol_name) #generate topology proposal topology_proposal = proposal_engine.propose(solvated_system, solvated_topology) #generate new positions with geometry engine new_positions, _ = geometry_engine.propose(topology_proposal, solvated_positions, beta) return topology_proposal, solvated_positions, new_positions
def test_alchemical_elimination_mutation(): """ Test alchemical elimination for mutations. """ ff_filename = "amber99sbildn.xml" proposal_metadata = {'ffxmls': [ff_filename]} # Create peptide. from openmmtools import testsystems testsystem = testsystems.AlanineDipeptideVacuum() [topology, system, positions ] = [testsystem.topology, testsystem.system, testsystem.positions] # Create forcefield. ff = app.ForceField(ff_filename) chain_id = ' ' allowed_mutations = [[('2', 'GLY')]] from perses.rjmc.topology_proposal import SystemGenerator system_generator = SystemGenerator([ff_filename]) # Create a topology proposal fro mutating ALA -> GLY from perses.rjmc.topology_proposal import PointMutationEngine proposal_engine = PointMutationEngine(topology, system_generator, chain_id, proposal_metadata=proposal_metadata, allowed_mutations=allowed_mutations) topology_proposal = proposal_engine.propose(system, topology) # Modify atom mapping to get a null transformation. from perses.rjmc.topology_proposal import TopologyProposal new_to_old_atom_map = { atom1: atom1 for atom1 in topology_proposal.new_to_old_atom_map } topology_proposal = TopologyProposal( new_topology=topology_proposal.old_topology, new_system=topology_proposal.old_system, old_topology=topology_proposal.old_topology, old_system=topology_proposal.old_system, old_chemical_state_key='AA', new_chemical_state_key='AG', logp_proposal=0.0, new_to_old_atom_map=new_to_old_atom_map, metadata=topology_proposal.metadata) for ncmc_nsteps in [0, 1, 2, 50]: f = partial(check_alchemical_null_elimination, topology_proposal, positions, ncmc_nsteps=ncmc_nsteps) f.description = "Testing alchemical null transformation of ALA sidechain in alanine dipeptide with %d NCMC steps" % ncmc_nsteps yield f
def generate_atp(phase='vacuum'): """ modify the AlanineDipeptideVacuum test system to be parametrized with amber14ffsb in vac or solvent (tip3p) """ import openmmtools.testsystems as ts atp = ts.AlanineDipeptideVacuum(constraints=app.HBonds, hydrogenMass=4 * unit.amus) forcefield_files = [ 'gaff.xml', 'amber14/protein.ff14SB.xml', 'amber14/tip3p.xml' ] if phase == 'vacuum': barostat = None system_generator = SystemGenerator(forcefield_files, barostat=barostat, forcefield_kwargs={ 'removeCMMotion': False, 'ewaldErrorTolerance': 1e-4, 'nonbondedMethod': app.NoCutoff, 'constraints': app.HBonds, 'hydrogenMass': 4 * unit.amus }) atp.system = system_generator.build_system( atp.topology) #update the parametrization scheme to amberff14sb elif phase == 'solvent': barostat = openmm.MonteCarloBarostat(1.0 * unit.atmosphere, 300 * unit.kelvin, 50) system_generator = SystemGenerator(forcefield_files, barostat=barostat, forcefield_kwargs={ 'removeCMMotion': False, 'ewaldErrorTolerance': 1e-4, 'nonbondedMethod': app.PME, 'constraints': app.HBonds, 'hydrogenMass': 4 * unit.amus }) if phase == 'solvent': modeller = app.Modeller(atp.topology, atp.positions) modeller.addSolvent(system_generator._forcefield, model='tip3p', padding=9 * unit.angstroms, ionicStrength=0.15 * unit.molar) solvated_topology = modeller.getTopology() solvated_positions = modeller.getPositions() # canonicalize the solvated positions: turn tuples into np.array atp.positions = unit.quantity.Quantity(value=np.array([ list(atom_pos) for atom_pos in solvated_positions.value_in_unit_system(unit.md_unit_system) ]), unit=unit.nanometers) atp.topology = solvated_topology atp.system = system_generator.build_system(atp.topology) return atp, system_generator
def generate_vacuum_topology_proposal(current_mol_name="benzene", proposed_mol_name="toluene"): """ Generate a test vacuum topology proposal, current positions, and new positions triplet from two IUPAC molecule names. Parameters ---------- current_mol_name : str, optional name of the first molecule proposed_mol_name : str, optional name of the second molecule Returns ------- topology_proposal : perses.rjmc.topology_proposal The topology proposal representing the transformation current_positions : np.array, unit-bearing The positions of the initial system new_positions : np.array, unit-bearing The positions of the new system """ from openmoltools import forcefield_generators from perses.tests.utils import createOEMolFromIUPAC, createSystemFromIUPAC, get_data_filename current_mol, unsolv_old_system, pos_old, top_old = createSystemFromIUPAC(current_mol_name) proposed_mol = createOEMolFromIUPAC(proposed_mol_name) initial_smiles = oechem.OEMolToSmiles(current_mol) final_smiles = oechem.OEMolToSmiles(proposed_mol) gaff_xml_filename = get_data_filename("data/gaff.xml") forcefield = app.ForceField(gaff_xml_filename, 'tip3p.xml') forcefield.registerTemplateGenerator(forcefield_generators.gaffTemplateGenerator) solvated_system = forcefield.createSystem(top_old, removeCMMotion=False) gaff_filename = get_data_filename('data/gaff.xml') system_generator = SystemGenerator([gaff_filename, 'amber99sbildn.xml', 'tip3p.xml'], forcefield_kwargs={'removeCMMotion': False, 'nonbondedMethod': app.NoCutoff}) geometry_engine = geometry.FFAllAngleGeometryEngine() proposal_engine = SmallMoleculeSetProposalEngine( [initial_smiles, final_smiles], system_generator, residue_name=current_mol_name) #generate topology proposal topology_proposal = proposal_engine.propose(solvated_system, top_old, current_mol=current_mol, proposed_mol=proposed_mol) #generate new positions with geometry engine new_positions, _ = geometry_engine.propose(topology_proposal, pos_old, beta) return topology_proposal, pos_old, new_positions
def __init__(self, **kwargs): super(Selectivity, self).__init__(**kwargs) solvents = ['explicit'] # DEBUG components = ['src-imatinib', 'abl-imatinib'] # TODO: Add 'ATP:kinase' complex to enable resistance design padding = 9.0*unit.angstrom explicit_solvent_model = 'tip3p' setup_path = 'data/abl-src' thermodynamic_states = dict() temperature = 300*unit.kelvin pressure = 1.0*unit.atmospheres geometry_engine = geometry.FFAllAngleGeometryEngine() # Construct list of all environments environments = list() for solvent in solvents: for component in components: environment = solvent + '-' + component environments.append(environment) # Read SMILES from CSV file of clinical kinase inhibitors. from pkg_resources import resource_filename smiles_filename = resource_filename('perses', 'data/clinical-kinase-inhibitors.csv') import csv molecules = list() with open(smiles_filename, 'r') as csvfile: csvreader = csv.reader(csvfile, delimiter=',', quotechar='"') for row in csvreader: name = row[0] smiles = row[1] molecules.append(smiles) # Add current molecule molecules.append('Cc1ccc(cc1Nc2nccc(n2)c3cccnc3)NC(=O)c4ccc(cc4)C[NH+]5CCN(CC5)C') self.molecules = molecules # Expand molecules without explicit stereochemistry and make canonical isomeric SMILES. molecules = sanitizeSMILES(self.molecules) # Create a system generator for desired forcefields from perses.rjmc.topology_proposal import SystemGenerator from pkg_resources import resource_filename gaff_xml_filename = resource_filename('perses', 'data/gaff.xml') system_generators = dict() system_generators['explicit'] = SystemGenerator([gaff_xml_filename, 'amber99sbildn.xml', 'tip3p.xml'], forcefield_kwargs={'nonbondedCutoff' : 9.0 * unit.angstrom, 'implicitSolvent' : None, 'constraints' : None },periodic_forcefield_kwargs = {'nonbondedMethod': app.CutoffPeriodic}) use_antechamber=True)
def __init__(self): super(abl_src_affinity, self).__init__() #solvents = ['vacuum', 'explicit'] # TODO: Add 'implicit' once GBSA parameterization for small molecules is working solvents = ['explicit'] # DEBUG components = [ 'src-imatinib', 'abl-imatinib' ] # TODO: Add 'ATP:kinase' complex to enable resistance design padding = 9.0 * unit.angstrom explicit_solvent_model = 'tip3p' setup_path = 'data/abl-src' thermodynamic_states = dict() temperature = 300 * unit.kelvin pressure = 1.0 * unit.atmospheres # Construct list of all environments environments = list() for solvent in solvents: for component in components: environment = solvent + '-' + component environments.append(environment) # Read SMILES from CSV file of clinical kinase inhibitors. from pkg_resources import resource_filename smiles_filename = resource_filename( 'perses', 'data/clinical-kinase-inhibitors.csv') import csv molecules = list() with open(smiles_filename, 'r') as csvfile: csvreader = csv.reader(csvfile, delimiter=',', quotechar='"') for row in csvreader: name = row[0] smiles = row[1] molecules.append(smiles) # Add current molecule molecules.append( 'Cc1ccc(cc1Nc2nccc(n2)c3cccnc3)NC(=O)c4ccc(cc4)C[NH+]5CCN(CC5)C') self.molecules = molecules # Expand molecules without explicit stereochemistry and make canonical isomeric SMILES. molecules = sanitizeSMILES(self.molecules) # Create a system generator for desired forcefields from perses.rjmc.topology_proposal import SystemGenerator from pkg_resources import resource_filename gaff_xml_filename = resource_filename('perses', 'data/gaff.xml') system_generators = dict() system_generators['explicit'] = SystemGenerator( [gaff_xml_filename, 'amber99sbildn.xml', 'tip3p.xml'], forcefield_kwargs={ 'nonbondedMethod': app.CutoffPeriodic, 'nonbondedCutoff': 9.0 * unit.angstrom, 'implicitSolvent': None, 'constraints': None }, use_antechamber=True) system_generators['implicit'] = SystemGenerator( [gaff_xml_filename, 'amber99sbildn.xml', 'amber99_obc.xml'], forcefield_kwargs={ 'nonbondedMethod': app.NoCutoff, 'implicitSolvent': app.OBC2, 'constraints': None }, use_antechamber=True) system_generators['vacuum'] = SystemGenerator( [gaff_xml_filename, 'amber99sbildn.xml'], forcefield_kwargs={ 'nonbondedMethod': app.NoCutoff, 'implicitSolvent': None, 'constraints': None }, use_antechamber=True) # Copy system generators for all environments for solvent in solvents: for component in components: environment = solvent + '-' + component system_generators[environment] = system_generators[solvent] # Load topologies and positions for all components from simtk.openmm.app import PDBFile, Modeller topologies = dict() positions = dict() for component in components: pdb_filename = resource_filename( 'perses', os.path.join(setup_path, '%s.pdb' % component)) print(pdb_filename) pdbfile = PDBFile(pdb_filename) topologies[component] = pdbfile.topology positions[component] = pdbfile.positions # Construct positions and topologies for all solvent environments for solvent in solvents: for component in components: environment = solvent + '-' + component if solvent == 'explicit': # Create MODELLER object. modeller = app.Modeller(topologies[component], positions[component]) modeller.addSolvent( system_generators[solvent].getForceField(), model='tip3p', padding=9.0 * unit.angstrom) topologies[environment] = modeller.getTopology() positions[environment] = modeller.getPositions() else: environment = solvent + '-' + component topologies[environment] = topologies[component] positions[environment] = positions[component] # Set up the proposal engines. from perses.rjmc.topology_proposal import SmallMoleculeSetProposalEngine proposal_metadata = {} proposal_engines = dict() for environment in environments: proposal_engines[environment] = SmallMoleculeSetProposalEngine( molecules, system_generators[environment], residue_name='MOL') # Generate systems systems = dict() for environment in environments: systems[environment] = system_generators[environment].build_system( topologies[environment]) # Define thermodynamic state of interest. from perses.samplers.thermodynamics import ThermodynamicState thermodynamic_states = dict() temperature = 300 * unit.kelvin pressure = 1.0 * unit.atmospheres for component in components: for solvent in solvents: environment = solvent + '-' + component if solvent == 'explicit': thermodynamic_states[environment] = ThermodynamicState( system=systems[environment], temperature=temperature, pressure=pressure) else: thermodynamic_states[environment] = ThermodynamicState( system=systems[environment], temperature=temperature) # Create SAMS samplers from perses.samplers.samplers import SamplerState, MCMCSampler, ExpandedEnsembleSampler, SAMSSampler mcmc_samplers = dict() exen_samplers = dict() sams_samplers = dict() for solvent in solvents: for component in components: environment = solvent + '-' + component chemical_state_key = proposal_engines[ environment].compute_state_key(topologies[environment]) if solvent == 'explicit': thermodynamic_state = ThermodynamicState( system=systems[environment], temperature=temperature, pressure=pressure) sampler_state = SamplerState( system=systems[environment], positions=positions[environment], box_vectors=systems[environment]. getDefaultPeriodicBoxVectors()) else: thermodynamic_state = ThermodynamicState( system=systems[environment], temperature=temperature) sampler_state = SamplerState( system=systems[environment], positions=positions[environment]) mcmc_samplers[environment] = MCMCSampler( thermodynamic_state, sampler_state) mcmc_samplers[ environment].nsteps = 5 # reduce number of steps for testing mcmc_samplers[environment].verbose = True exen_samplers[environment] = ExpandedEnsembleSampler( mcmc_samplers[environment], topologies[environment], chemical_state_key, proposal_engines[environment], options={'nsteps': 5}) exen_samplers[environment].verbose = True sams_samplers[environment] = SAMSSampler( exen_samplers[environment]) sams_samplers[environment].verbose = True thermodynamic_states[environment] = thermodynamic_state # Create test MultiTargetDesign sampler. # TODO: Replace this with inhibitor:kinase and ATP:kinase ratio from perses.samplers.samplers import MultiTargetDesign target_samplers = { sams_samplers['explicit-src-imatinib']: 1.0, sams_samplers['explicit-abl-imatinib']: -1.0 } designer = MultiTargetDesign(target_samplers) designer.verbose = True # Store things. self.molecules = molecules self.environments = environments self.topologies = topologies self.positions = positions self.system_generators = system_generators self.systems = systems self.proposal_engines = proposal_engines self.thermodynamic_states = thermodynamic_states self.mcmc_samplers = mcmc_samplers self.exen_samplers = exen_samplers self.sams_samplers = sams_samplers self.designer = designer # This system must currently be minimized. minimize(self)
def generate_vacuum_hostguest_proposal(current_mol_name="B2", proposed_mol_name="MOL"): """ Generate a test vacuum topology proposal, current positions, and new positions triplet from two IUPAC molecule names. Parameters ---------- current_mol_name : str, optional name of the first molecule proposed_mol_name : str, optional name of the second molecule Returns ------- topology_proposal : perses.rjmc.topology_proposal The topology proposal representing the transformation current_positions : np.array, unit-bearing The positions of the initial system new_positions : np.array, unit-bearing The positions of the new system """ from openmoltools import forcefield_generators from openmmtools import testsystems from perses.utils.openeye import smiles_to_oemol from perses.utils.data import get_data_filename host_guest = testsystems.HostGuestVacuum() unsolv_old_system, old_positions, top_old = host_guest.system, host_guest.positions, host_guest.topology ligand_topology = [res for res in top_old.residues()] current_mol = forcefield_generators.generateOEMolFromTopologyResidue( ligand_topology[1]) # guest is second residue in topology proposed_mol = smiles_to_oemol('C1CC2(CCC1(CC2)C)C') initial_smiles = oechem.OEMolToSmiles(current_mol) final_smiles = oechem.OEMolToSmiles(proposed_mol) gaff_xml_filename = get_data_filename("data/gaff.xml") forcefield = app.ForceField(gaff_xml_filename, 'tip3p.xml') forcefield.registerTemplateGenerator( forcefield_generators.gaffTemplateGenerator) solvated_system = forcefield.createSystem(top_old, removeCMMotion=False) gaff_filename = get_data_filename('data/gaff.xml') system_generator = SystemGenerator( [gaff_filename, 'amber99sbildn.xml', 'tip3p.xml'], forcefield_kwargs={ 'removeCMMotion': False, 'nonbondedMethod': app.NoCutoff }) geometry_engine = geometry.FFAllAngleGeometryEngine() proposal_engine = SmallMoleculeSetProposalEngine( [initial_smiles, final_smiles], system_generator, residue_name=current_mol_name) #generate topology proposal topology_proposal = proposal_engine.propose(solvated_system, top_old, current_mol=current_mol, proposed_mol=proposed_mol) #generate new positions with geometry engine new_positions, _ = geometry_engine.propose(topology_proposal, old_positions, beta) return topology_proposal, old_positions, new_positions
def generate_solvated_hybrid_test_topology(current_mol_name="naphthalene", proposed_mol_name="benzene", current_mol_smiles=None, proposed_mol_smiles=None, vacuum=False, render_atom_mapping=False): """ This function will generate a topology proposal, old positions, and new positions with a geometry proposal (either vacuum or solvated) given a set of input iupacs or smiles. The function will (by default) read the iupac names first. If they are set to None, then it will attempt to read a set of current and new smiles. An atom mapping pdf will be generated if specified. Arguments ---------- current_mol_name : str, optional name of the first molecule proposed_mol_name : str, optional name of the second molecule current_mol_smiles : str (default None) current mol smiles proposed_mol_smiles : str (default None) proposed mol smiles vacuum: bool (default False) whether to render a vacuum or solvated topology_proposal render_atom_mapping : bool (default False) whether to render the atom map of the current_mol_name and proposed_mol_name Returns ------- topology_proposal : perses.rjmc.topology_proposal The topology proposal representing the transformation current_positions : np.array, unit-bearing The positions of the initial system new_positions : np.array, unit-bearing The positions of the new system """ import simtk.openmm.app as app from openmoltools import forcefield_generators from openeye import oechem from openmoltools.openeye import iupac_to_oemol, generate_conformers, smiles_to_oemol from openmoltools import forcefield_generators import perses.utils.openeye as openeye from perses.utils.data import get_data_filename from perses.rjmc.topology_proposal import TopologyProposal, SystemGenerator, SmallMoleculeSetProposalEngine import simtk.unit as unit from perses.rjmc.geometry import FFAllAngleGeometryEngine if current_mol_name != None and proposed_mol_name != None: try: old_oemol, new_oemol = iupac_to_oemol( current_mol_name), iupac_to_oemol(proposed_mol_name) old_smiles = oechem.OECreateSmiString( old_oemol, oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens) new_smiles = oechem.OECreateSmiString( new_oemol, oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens) except: raise Exception( f"either {current_mol_name} or {proposed_mol_name} is not compatible with 'iupac_to_oemol' function!" ) elif current_mol_smiles != None and proposed_mol_smiles != None: try: old_oemol, new_oemol = smiles_to_oemol( current_mol_smiles), smiles_to_oemol(proposed_mol_smiles) old_smiles = oechem.OECreateSmiString( old_oemol, oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens) new_smiles = oechem.OECreateSmiString( new_oemol, oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens) except: raise Exception(f"the variables are not compatible") else: raise Exception( f"either current_mol_name and proposed_mol_name must be specified as iupacs OR current_mol_smiles and proposed_mol_smiles must be specified as smiles strings." ) old_oemol, old_system, old_positions, old_topology = openeye.createSystemFromSMILES( old_smiles, title="MOL") #correct the old positions old_positions = openeye.extractPositionsFromOEMol(old_oemol) old_positions = old_positions.in_units_of(unit.nanometers) new_oemol, new_system, new_positions, new_topology = openeye.createSystemFromSMILES( new_smiles, title="NEW") ffxml = forcefield_generators.generateForceFieldFromMolecules( [old_oemol, new_oemol]) old_oemol.SetTitle('MOL') new_oemol.SetTitle('MOL') old_topology = forcefield_generators.generateTopologyFromOEMol(old_oemol) new_topology = forcefield_generators.generateTopologyFromOEMol(new_oemol) if not vacuum: nonbonded_method = app.PME barostat = openmm.MonteCarloBarostat(1.0 * unit.atmosphere, 300.0 * unit.kelvin, 50) else: nonbonded_method = app.NoCutoff barostat = None gaff_xml_filename = get_data_filename("data/gaff.xml") system_generator = SystemGenerator( [gaff_xml_filename, 'amber99sbildn.xml', 'tip3p.xml'], barostat=barostat, forcefield_kwargs={ 'removeCMMotion': False, 'nonbondedMethod': nonbonded_method, 'constraints': app.HBonds, 'hydrogenMass': 4.0 * unit.amu }) system_generator._forcefield.loadFile(StringIO(ffxml)) proposal_engine = SmallMoleculeSetProposalEngine([old_smiles, new_smiles], system_generator, residue_name='MOL') geometry_engine = FFAllAngleGeometryEngine(metadata=None, use_sterics=False, n_bond_divisions=1000, n_angle_divisions=180, n_torsion_divisions=360, verbose=True, storage=None, bond_softening_constant=1.0, angle_softening_constant=1.0, neglect_angles=False) if not vacuum: #now to solvate modeller = app.Modeller(old_topology, old_positions) hs = [ atom for atom in modeller.topology.atoms() if atom.element.symbol in ['H'] and atom.residue.name not in ['MOL', 'OLD', 'NEW'] ] modeller.delete(hs) modeller.addHydrogens(forcefield=system_generator._forcefield) modeller.addSolvent(system_generator._forcefield, model='tip3p', padding=9.0 * unit.angstroms) solvated_topology = modeller.getTopology() solvated_positions = modeller.getPositions() solvated_positions = unit.quantity.Quantity(value=np.array([ list(atom_pos) for atom_pos in solvated_positions.value_in_unit_system(unit.md_unit_system) ]), unit=unit.nanometers) solvated_system = system_generator.build_system(solvated_topology) #now to create proposal top_proposal = proposal_engine.propose( current_system=solvated_system, current_topology=solvated_topology, current_mol=old_oemol, proposed_mol=new_oemol) new_positions, _ = geometry_engine.propose(top_proposal, solvated_positions, beta) if render_atom_mapping: from perses.utils.smallmolecules import render_atom_mapping print( f"new_to_old: {proposal_engine.non_offset_new_to_old_atom_map}" ) render_atom_mapping(f"{old_smiles}to{new_smiles}.png", old_oemol, new_oemol, proposal_engine.non_offset_new_to_old_atom_map) return top_proposal, solvated_positions, new_positions else: vacuum_system = system_generator.build_system(old_topology) top_proposal = proposal_engine.propose(current_system=vacuum_system, current_topology=old_topology, current_mol=old_oemol, proposed_mol=new_oemol) new_positions, _ = geometry_engine.propose(top_proposal, old_positions, beta) if render_atom_mapping: from perses.utils.smallmolecules import render_atom_mapping print(f"new_to_old: {top_proposal._new_to_old_atom_map}") render_atom_mapping(f"{old_smiles}to{new_smiles}.png", old_oemol, new_oemol, top_proposal._new_to_old_atom_map) return top_proposal, old_positions, new_positions
# In[ ]: from perses.annihilation.relative import HybridTopologyFactory from perses.annihilation.lambda_protocol import RelativeAlchemicalState, LambdaProtocol from perses.rjmc.topology_proposal import TopologyProposal, TwoMoleculeSetProposalEngine, SystemGenerator, SmallMoleculeSetProposalEngine, PointMutationEngine from perses.rjmc.geometry import FFAllAngleGeometryEngine from perses.utils.openeye import createSystemFromSMILES # In[ ]: system_generator = SystemGenerator( ['gaff2.xml'], barostat=None, forcefield_kwargs={ 'removeCMMotion': False, 'ewaldErrorTolerance': 1e-4, 'nonbondedMethod': app.NoCutoff, 'constraints': app.HBonds, 'hydrogenMass': 4 * unit.amus }) # In[ ]: # In[ ]: old_mol, old_system, old_pos, old_top = createSystemFromSMILES('CCCCO') old_system = system_generator.build_system(old_top) new_mol, new_system, new_pos, new_top = createSystemFromSMILES('CCCCS') new_system = system_generator.build_system(new_top)
def __init__(self, molecules: List[str], output_filename: str, ncmc_switching_times: Dict[str, int], equilibrium_steps: Dict[str, int], timestep: unit.Quantity, initial_molecule: str=None, geometry_options: Dict=None): self._molecules = [SmallMoleculeSetProposalEngine.canonicalize_smiles(molecule) for molecule in molecules] environments = ['explicit', 'vacuum'] temperature = 298.15 * unit.kelvin pressure = 1.0 * unit.atmospheres constraints = app.HBonds self._storage = NetCDFStorage(output_filename) self._ncmc_switching_times = ncmc_switching_times self._n_equilibrium_steps = equilibrium_steps self._geometry_options = geometry_options # Create a system generator for our desired forcefields. from perses.rjmc.topology_proposal import SystemGenerator system_generators = dict() from pkg_resources import resource_filename gaff_xml_filename = resource_filename('perses', 'data/gaff.xml') barostat = openmm.MonteCarloBarostat(pressure, temperature) system_generators['explicit'] = SystemGenerator([gaff_xml_filename, 'tip3p.xml'], forcefield_kwargs={'nonbondedCutoff': 9.0 * unit.angstrom, 'implicitSolvent': None, 'constraints': constraints, 'ewaldErrorTolerance': 1e-5, 'hydrogenMass': 3.0*unit.amu}, periodic_forcefield_kwargs = {'nonbondedMethod': app.PME} barostat=barostat) system_generators['vacuum'] = SystemGenerator([gaff_xml_filename], forcefield_kwargs={'implicitSolvent': None, 'constraints': constraints, 'hydrogenMass': 3.0*unit.amu}, nonperiodic_forcefield_kwargs = {'nonbondedMethod': app.NoCutoff}) # # Create topologies and positions # topologies = dict() positions = dict() from openmoltools import forcefield_generators forcefield = app.ForceField(gaff_xml_filename, 'tip3p.xml') forcefield.registerTemplateGenerator(forcefield_generators.gaffTemplateGenerator) # Create molecule in vacuum. from perses.utils.openeye import extractPositionsFromOEMol from openmoltools.openeye import smiles_to_oemol, generate_conformers if initial_molecule: smiles = initial_molecule else: smiles = np.random.choice(molecules) molecule = smiles_to_oemol(smiles) molecule = generate_conformers(molecule, max_confs=1) topologies['vacuum'] = forcefield_generators.generateTopologyFromOEMol(molecule) positions['vacuum'] = extractPositionsFromOEMol(molecule) # Create molecule in solvent. modeller = app.Modeller(topologies['vacuum'], positions['vacuum']) modeller.addSolvent(forcefield, model='tip3p', padding=9.0 * unit.angstrom) topologies['explicit'] = modeller.getTopology() positions['explicit'] = modeller.getPositions() # Set up the proposal engines. proposal_metadata = {} proposal_engines = dict() for environment in environments: proposal_engines[environment] = SmallMoleculeSetProposalEngine(self._molecules, system_generators[environment]) # Generate systems systems = dict() for environment in environments: systems[environment] = system_generators[environment].build_system(topologies[environment]) # Define thermodynamic state of interest. thermodynamic_states = dict() thermodynamic_states['explicit'] = states.ThermodynamicState(system=systems['explicit'], temperature=temperature, pressure=pressure) thermodynamic_states['vacuum'] = states.ThermodynamicState(system=systems['vacuum'], temperature=temperature) # Create SAMS samplers from perses.samplers.samplers import ExpandedEnsembleSampler, SAMSSampler mcmc_samplers = dict() exen_samplers = dict() sams_samplers = dict() for environment in environments: storage = NetCDFStorageView(self._storage, envname=environment) if self._geometry_options: n_torsion_divisions = self._geometry_options['n_torsion_divsions'][environment] use_sterics = self._geometry_options['use_sterics'][environment] else: n_torsion_divisions = 180 use_sterics = False geometry_engine = geometry.FFAllAngleGeometryEngine(storage=storage, n_torsion_divisions=n_torsion_divisions, use_sterics=use_sterics) move = mcmc.LangevinSplittingDynamicsMove(timestep=timestep, splitting="V R O R V", n_restart_attempts=10) chemical_state_key = proposal_engines[environment].compute_state_key(topologies[environment]) if environment == 'explicit': sampler_state = states.SamplerState(positions=positions[environment], box_vectors=systems[environment].getDefaultPeriodicBoxVectors()) else: sampler_state = states.SamplerState(positions=positions[environment]) mcmc_samplers[environment] = mcmc.MCMCSampler(thermodynamic_states[environment], sampler_state, move) exen_samplers[environment] = ExpandedEnsembleSampler(mcmc_samplers[environment], topologies[environment], chemical_state_key, proposal_engines[environment], geometry_engine, options={'nsteps': self._ncmc_switching_times[environment]}, storage=storage, ncmc_write_interval=self._ncmc_switching_times[environment]) exen_samplers[environment].verbose = True sams_samplers[environment] = SAMSSampler(exen_samplers[environment], storage=storage) sams_samplers[environment].verbose = True # Create test MultiTargetDesign sampler. from perses.samplers.samplers import MultiTargetDesign target_samplers = {sams_samplers['explicit']: 1.0, sams_samplers['vacuum']: -1.0} designer = MultiTargetDesign(target_samplers, storage=self._storage) # Store things. self.molecules = molecules self.environments = environments self.topologies = topologies self.positions = positions self.system_generators = system_generators self.proposal_engines = proposal_engines self.thermodynamic_states = thermodynamic_states self.mcmc_samplers = mcmc_samplers self.exen_samplers = exen_samplers self.sams_samplers = sams_samplers self.designer = designer
def generate_topology_proposal(old_mol_iupac="pentane", new_mol_iupac="butane"): """ Utility function to generate a topologyproposal for tests Parameters ---------- old_mol_iupac : str, optional name of old mol, default pentane new_mol_iupac : str, optional name of new mol, default butane Returns ------- topology_proposal : perses.rjmc.topology_proposal.TopologyProposal the topology proposal corresponding to the given transformation old_positions : [n, 3] np.ndarray of float positions of old mol new_positions : [m, 3] np.ndarray of float positions of new mol """ from perses.rjmc.topology_proposal import TwoMoleculeSetProposalEngine, SystemGenerator from perses.rjmc.geometry import FFAllAngleGeometryEngine from perses.tests.utils import createSystemFromIUPAC, get_data_filename import openmoltools.forcefield_generators as forcefield_generators from io import StringIO from openmmtools.constants import kB temperature = 300.0 * unit.kelvin kT = kB * temperature beta = 1.0 / kT gaff_filename = get_data_filename("data/gaff.xml") forcefield_files = [gaff_filename, 'amber99sbildn.xml'] #generate systems and topologies old_mol, old_system, old_positions, old_topology = createSystemFromIUPAC( old_mol_iupac) new_mol, new_system, new_positions, new_topology = createSystemFromIUPAC( new_mol_iupac) #set names old_mol.SetTitle("MOL") new_mol.SetTitle("MOL") #generate forcefield and ProposalEngine #ffxml=forcefield_generators.generateForceFieldFromMolecules([old_mol, new_mol]) system_generator = SystemGenerator( forcefield_files, forcefield_kwargs={'removeCMMotion': False}) proposal_engine = TwoMoleculeSetProposalEngine(old_mol, new_mol, system_generator, residue_name="pentane") geometry_engine = FFAllAngleGeometryEngine() #create a TopologyProposal topology_proposal = proposal_engine.propose(old_system, old_topology) new_positions_geometry, _ = geometry_engine.propose( topology_proposal, old_positions, beta) return topology_proposal, old_positions, new_positions_geometry
def create_systems(topologies_dict, positions_dict, output_directory, project_prefix, solvate=True): """ Generate the systems ready for equilibrium simulations from a dictionary of topologies and positions Parameters ---------- topologies_dict : dict of str: app.Topoology A dictionary of the topologies to prepare, indexed by SMILES strings positions_dict : dict of str: unit.Quantity array A dictionary of positions for the corresponding topologies, indexed by SMILES strings output_directory : str Location of output files project_prefix : str What to prepend to the names of files for this run solvate : bool, default True Whether to solvate the systems """ barostat = openmm.MonteCarloBarostat(1.0 * unit.atmosphere, temperature, 50) system_generator = SystemGenerator( [ 'amber14/protein.ff14SB.xml', 'gaff.xml', 'amber14/tip3p.xml', 'MCL1_ligands.xml' ], barostat=barostat, forcefield_kwargs={ 'nonbondedMethod': app.PME, 'constraints': app.HBonds, 'hydrogenMass': 4 * unit.amus }, use_antechamber=False) list_of_smiles = list(topologies_dict.keys()) initial_smiles = list_of_smiles[0] initial_topology = topologies_dict[initial_smiles] initial_positions = positions_dict[initial_smiles] if solvate: solvated_initial_positions, solvated_topology, solvated_system = solvate_system( initial_topology.to_openmm(), initial_positions, system_generator) else: solvated_initial_positions = initial_positions solvated_topology = initial_topology solvated_system = system_generator.build_system(solvated_topology) md_topology = md.Topology.from_openmm(solvated_topology) if solvate: num_added = md_topology.n_residues - initial_topology.n_residues if not os.path.exists(output_directory): os.mkdir(output_directory) np.save("{}/{}_{}_initial.npy".format(output_directory, project_prefix, 0), (solvated_initial_positions, md_topology, solvated_system, initial_smiles)) for i in tqdm.trange(1, len(list_of_smiles)): smiles = list_of_smiles[i] topology = topologies_dict[smiles] positions = positions_dict[smiles] if solvate: solvated_positions, solvated_topology, solvated_system = solvate_system( topology.to_openmm(), positions, system_generator, padding=None, num_added=num_added) else: solvated_positions = initial_positions solvated_topology = initial_topology solvated_system = system_generator.build_system(solvated_topology) np.save( "{}/{}_{}_initial.npy".format(output_directory, project_prefix, i), (solvated_positions, md.Topology.from_openmm(solvated_topology), solvated_system, smiles))
def __init__(self, protein_pdb_filename, ligand_file, old_ligand_index, new_ligand_index, forcefield_files, pressure=1.0 * unit.atmosphere, temperature=300.0 * unit.kelvin, solvent_padding=9.0 * unit.angstroms): """ Initialize a NonequilibriumFEPSetup object Parameters ---------- protein_pdb_filename : str The name of the protein pdb file ligand_file : str the name of the ligand file (any openeye supported format) ligand_smiles : list of two str The SMILES strings representing the two ligands forcefield_files : list of str The list of ffxml files that contain the forcefields that will be used pressure : Quantity, units of pressure Pressure to use in the barostat temperature : Quantity, units of temperature Temperature to use for the Langevin integrator solvent_padding : Quantity, units of length The amount of padding to use when adding solvent """ self._protein_pdb_filename = protein_pdb_filename self._pressure = pressure self._temperature = temperature self._barostat_period = 50 self._padding = solvent_padding self._ligand_file = ligand_file self._old_ligand_index = old_ligand_index self._new_ligand_index = new_ligand_index self._old_ligand_oemol = self.load_sdf(self._ligand_file, index=self._old_ligand_index) self._new_ligand_oemol = self.load_sdf(self._ligand_file, index=self._new_ligand_index) self._old_ligand_positions = extractPositionsFromOEMOL( self._old_ligand_oemol) ffxml = forcefield_generators.generateForceFieldFromMolecules( [self._old_ligand_oemol, self._new_ligand_oemol]) self._old_ligand_oemol.SetTitle("MOL") self._new_ligand_oemol.SetTitle("MOL") self._new_ligand_smiles = oechem.OECreateSmiString( self._new_ligand_oemol, oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens) #self._old_ligand_smiles = '[H]c1c(c(c(c(c1N([H])c2nc3c(c(n2)OC([H])([H])C4(C(C(C(C(C4([H])[H])([H])[H])([H])[H])([H])[H])([H])[H])[H])nc(n3[H])[H])[H])[H])S(=O)(=O)C([H])([H])[H])[H]' self._old_ligand_smiles = oechem.OECreateSmiString( self._old_ligand_oemol, oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens) print(self._new_ligand_smiles) print(self._old_ligand_smiles) self._old_ligand_topology = forcefield_generators.generateTopologyFromOEMol( self._old_ligand_oemol) self._old_ligand_md_topology = md.Topology.from_openmm( self._old_ligand_topology) self._new_ligand_topology = forcefield_generators.generateTopologyFromOEMol( self._new_ligand_oemol) self._new_liands_md_topology = md.Topology.from_openmm( self._new_ligand_topology) protein_pdbfile = open(self._protein_pdb_filename, 'r') pdb_file = app.PDBFile(protein_pdbfile) protein_pdbfile.close() self._protein_topology_old = pdb_file.topology self._protein_md_topology_old = md.Topology.from_openmm( self._protein_topology_old) self._protein_positions_old = pdb_file.positions self._forcefield = app.ForceField(*forcefield_files) self._forcefield.loadFile(StringIO(ffxml)) print("Generated forcefield") self._complex_md_topology_old = self._protein_md_topology_old.join( self._old_ligand_md_topology) self._complex_topology_old = self._complex_md_topology_old.to_openmm() n_atoms_complex_old = self._complex_topology_old.getNumAtoms() n_atoms_protein_old = self._protein_topology_old.getNumAtoms() self._complex_positions_old = unit.Quantity(np.zeros( [n_atoms_complex_old, 3]), unit=unit.nanometers) self._complex_positions_old[: n_atoms_protein_old, :] = self._protein_positions_old self._complex_positions_old[ n_atoms_protein_old:, :] = self._old_ligand_positions if pressure is not None: barostat = openmm.MonteCarloBarostat(self._pressure, self._temperature, self._barostat_period) self._system_generator = SystemGenerator( forcefield_files, barostat=barostat, forcefield_kwargs={'nonbondedMethod': app.PME}) else: self._system_generator = SystemGenerator(forcefield_files) #self._complex_proposal_engine = TwoMoleculeSetProposalEngine(self._old_ligand_smiles, self._new_ligand_smiles, self._system_generator, residue_name="MOL") self._complex_proposal_engine = TwoMoleculeSetProposalEngine( self._old_ligand_oemol, self._new_ligand_oemol, self._system_generator, residue_name="MOL") self._geometry_engine = FFAllAngleGeometryEngine() self._complex_topology_old_solvated, self._complex_positions_old_solvated, self._complex_system_old_solvated = self._solvate_system( self._complex_topology_old, self._complex_positions_old) self._complex_md_topology_old_solvated = md.Topology.from_openmm( self._complex_topology_old_solvated) print(self._complex_proposal_engine._smiles_list) beta = 1.0 / (kB * temperature) self._complex_topology_proposal = self._complex_proposal_engine.propose( self._complex_system_old_solvated, self._complex_topology_old_solvated) self._complex_positions_new_solvated, _ = self._geometry_engine.propose( self._complex_topology_proposal, self._complex_positions_old_solvated, beta) #now generate the equivalent objects for the solvent phase. First, generate the ligand-only topologies and atom map self._solvent_topology_proposal, self._old_solvent_positions = self._generate_ligand_only_topologies( self._complex_positions_old_solvated, self._complex_positions_new_solvated) self._new_solvent_positions, _ = self._geometry_engine.propose( self._solvent_topology_proposal, self._old_solvent_positions, beta)
class NonequilibriumFEPSetup(object): """ This class is a helper class for nonequilibrium FEP. It generates the input objects that are necessary for the two legs of a relative FEP calculation. For each leg, that is a TopologyProposal, old_positions, and new_positions. Importantly, it ensures that the atom maps in the solvent and complex phases match correctly. """ def __init__(self, protein_pdb_filename, ligand_file, old_ligand_index, new_ligand_index, forcefield_files, pressure=1.0 * unit.atmosphere, temperature=300.0 * unit.kelvin, solvent_padding=9.0 * unit.angstroms): """ Initialize a NonequilibriumFEPSetup object Parameters ---------- protein_pdb_filename : str The name of the protein pdb file ligand_file : str the name of the ligand file (any openeye supported format) ligand_smiles : list of two str The SMILES strings representing the two ligands forcefield_files : list of str The list of ffxml files that contain the forcefields that will be used pressure : Quantity, units of pressure Pressure to use in the barostat temperature : Quantity, units of temperature Temperature to use for the Langevin integrator solvent_padding : Quantity, units of length The amount of padding to use when adding solvent """ self._protein_pdb_filename = protein_pdb_filename self._pressure = pressure self._temperature = temperature self._barostat_period = 50 self._padding = solvent_padding self._ligand_file = ligand_file self._old_ligand_index = old_ligand_index self._new_ligand_index = new_ligand_index self._old_ligand_oemol = self.load_sdf(self._ligand_file, index=self._old_ligand_index) self._new_ligand_oemol = self.load_sdf(self._ligand_file, index=self._new_ligand_index) self._old_ligand_positions = extractPositionsFromOEMOL( self._old_ligand_oemol) ffxml = forcefield_generators.generateForceFieldFromMolecules( [self._old_ligand_oemol, self._new_ligand_oemol]) self._old_ligand_oemol.SetTitle("MOL") self._new_ligand_oemol.SetTitle("MOL") self._new_ligand_smiles = oechem.OECreateSmiString( self._new_ligand_oemol, oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens) #self._old_ligand_smiles = '[H]c1c(c(c(c(c1N([H])c2nc3c(c(n2)OC([H])([H])C4(C(C(C(C(C4([H])[H])([H])[H])([H])[H])([H])[H])([H])[H])[H])nc(n3[H])[H])[H])[H])S(=O)(=O)C([H])([H])[H])[H]' self._old_ligand_smiles = oechem.OECreateSmiString( self._old_ligand_oemol, oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens) print(self._new_ligand_smiles) print(self._old_ligand_smiles) self._old_ligand_topology = forcefield_generators.generateTopologyFromOEMol( self._old_ligand_oemol) self._old_ligand_md_topology = md.Topology.from_openmm( self._old_ligand_topology) self._new_ligand_topology = forcefield_generators.generateTopologyFromOEMol( self._new_ligand_oemol) self._new_liands_md_topology = md.Topology.from_openmm( self._new_ligand_topology) protein_pdbfile = open(self._protein_pdb_filename, 'r') pdb_file = app.PDBFile(protein_pdbfile) protein_pdbfile.close() self._protein_topology_old = pdb_file.topology self._protein_md_topology_old = md.Topology.from_openmm( self._protein_topology_old) self._protein_positions_old = pdb_file.positions self._forcefield = app.ForceField(*forcefield_files) self._forcefield.loadFile(StringIO(ffxml)) print("Generated forcefield") self._complex_md_topology_old = self._protein_md_topology_old.join( self._old_ligand_md_topology) self._complex_topology_old = self._complex_md_topology_old.to_openmm() n_atoms_complex_old = self._complex_topology_old.getNumAtoms() n_atoms_protein_old = self._protein_topology_old.getNumAtoms() self._complex_positions_old = unit.Quantity(np.zeros( [n_atoms_complex_old, 3]), unit=unit.nanometers) self._complex_positions_old[: n_atoms_protein_old, :] = self._protein_positions_old self._complex_positions_old[ n_atoms_protein_old:, :] = self._old_ligand_positions if pressure is not None: barostat = openmm.MonteCarloBarostat(self._pressure, self._temperature, self._barostat_period) self._system_generator = SystemGenerator( forcefield_files, barostat=barostat, forcefield_kwargs={'nonbondedMethod': app.PME}) else: self._system_generator = SystemGenerator(forcefield_files) #self._complex_proposal_engine = TwoMoleculeSetProposalEngine(self._old_ligand_smiles, self._new_ligand_smiles, self._system_generator, residue_name="MOL") self._complex_proposal_engine = TwoMoleculeSetProposalEngine( self._old_ligand_oemol, self._new_ligand_oemol, self._system_generator, residue_name="MOL") self._geometry_engine = FFAllAngleGeometryEngine() self._complex_topology_old_solvated, self._complex_positions_old_solvated, self._complex_system_old_solvated = self._solvate_system( self._complex_topology_old, self._complex_positions_old) self._complex_md_topology_old_solvated = md.Topology.from_openmm( self._complex_topology_old_solvated) print(self._complex_proposal_engine._smiles_list) beta = 1.0 / (kB * temperature) self._complex_topology_proposal = self._complex_proposal_engine.propose( self._complex_system_old_solvated, self._complex_topology_old_solvated) self._complex_positions_new_solvated, _ = self._geometry_engine.propose( self._complex_topology_proposal, self._complex_positions_old_solvated, beta) #now generate the equivalent objects for the solvent phase. First, generate the ligand-only topologies and atom map self._solvent_topology_proposal, self._old_solvent_positions = self._generate_ligand_only_topologies( self._complex_positions_old_solvated, self._complex_positions_new_solvated) self._new_solvent_positions, _ = self._geometry_engine.propose( self._solvent_topology_proposal, self._old_solvent_positions, beta) def load_sdf(self, sdf_filename, index=0): """ Load an SDF file into an OEMol. Since SDF files can contain multiple molecules, an index can be provided as well. Parameters ---------- sdf_filename : str The name of the SDF file index : int, default 0 The index of the molecule in the SDF file Returns ------- mol : openeye.oechem.OEMol object The loaded oemol object """ ifs = oechem.oemolistream() ifs.open(sdf_filename) #get the list of molecules mol_list = [oechem.OEMol(mol) for mol in ifs.GetOEMols()] #we'll always take the first for now mol_to_return = mol_list[index] return mol_to_return def _solvate_system(self, topology, positions, model='tip3p'): """ Generate a solvated topology, positions, and system for a given input topology and positions. For generating the system, the forcefield files provided in the constructor will be used. Parameters ---------- topology : app.Topology Topology of the system to solvate positions : [n, 3] ndarray of Quantity nm the positions of the unsolvated system Returns ------- solvated_topology : app.Topology Topology of the system with added waters solvated_positions : [n + 3(n_waters), 3] ndarray of Quantity nm Solvated positions solvated_system : openmm.System The parameterized system, containing a barostat if one was specified. """ modeller = app.Modeller(topology, positions) hs = [ atom for atom in modeller.topology.atoms() if atom.element.symbol in ['H'] and atom.residue.name != "MOL" ] modeller.delete(hs) modeller.addHydrogens(forcefield=self._forcefield) print("preparing to add solvent") modeller.addSolvent(self._forcefield, model=model, padding=self._padding) solvated_topology = modeller.getTopology() solvated_positions = modeller.getPositions() print("solvent added, parameterizing") solvated_system = self._system_generator.build_system( solvated_topology) print("System parameterized") return solvated_topology, solvated_positions, solvated_system def _generate_ligand_only_topologies(self, old_positions, new_positions): """ This method generates ligand-only topologies and positions from a TopologyProposal containing a solvated complex. The output of this method is then used when building the solvent-phase simulation with the same atom map. Parameters ---------- topology_proposal : perses.rjmc.TopologyProposal TopologyProposal representing the solvated complex transformation Returns ------- old_ligand_topology : app.Topology The old topology without the receptor or solvent new_ligand_topology : app.Topology The new topology without the receptor or solvent old_ligand_positions : [m, 3] ndarray of Quantity nm The positions of the old ligand without receptor or solvent new_ligand_positions : [n, 3] ndarray of Quantity nm The positions of the new ligand without receptor or solvent atom_map : dict of int: it The mapping between the two topologies without ligand or solvent. """ old_complex = md.Topology.from_openmm( self._complex_topology_proposal.old_topology) new_complex = md.Topology.from_openmm( self._complex_topology_proposal.new_topology) complex_atom_map = self._complex_topology_proposal.old_to_new_atom_map old_mol_start_index, old_mol_len = self._complex_proposal_engine._find_mol_start_index( old_complex.to_openmm()) new_mol_start_index, new_mol_len = self._complex_proposal_engine._find_mol_start_index( new_complex.to_openmm()) old_pos = unit.Quantity(np.zeros([len(old_positions), 3]), unit=unit.nanometers) old_pos[:, :] = old_positions old_ligand_positions = old_pos[old_mol_start_index:( old_mol_start_index + old_mol_len), :] new_ligand_positions = new_positions[new_mol_start_index:( new_mol_start_index + new_mol_len), :] #atom_map_adjusted = {} #loop through the atoms in the map. If the old index is creater than the old_mol_start_index but less than that #plus the old mol length, then it is valid to include its adjusted value in the map. #for old_idx, new_idx in complex_atom_map.items(): # if old_idx > old_mol_start_index and old_idx < old_mol_len + old_mol_start_index: # atom_map_adjusted[old_idx - old_mol_len] = new_idx - new_mol_start_index #subset the topologies: old_ligand_topology = old_complex.subset( old_complex.select("resname == 'MOL' ")) new_ligand_topology = new_complex.subset( new_complex.select("resname == 'MOL' ")) #solvate the old ligand topology: old_solvated_topology, old_solvated_positions, old_solvated_system = self._solvate_system( old_ligand_topology.to_openmm(), old_ligand_positions) old_solvated_md_topology = md.Topology.from_openmm( old_solvated_topology) #now remove the old ligand, leaving only the solvent solvent_only_topology = old_solvated_md_topology.subset( old_solvated_md_topology.select("water")) #append the solvent to the new ligand-only topology: new_solvated_ligand_md_topology = new_ligand_topology.join( solvent_only_topology) nsl, b = new_solvated_ligand_md_topology.to_dataframe() #dirty hack because new_solvated_ligand_md_topology.to_openmm() was throwing bond topology error new_solvated_ligand_md_topology = md.Topology.from_dataframe(nsl, b) new_solvated_ligand_omm_topology = new_solvated_ligand_md_topology.to_openmm( ) new_solvated_ligand_omm_topology.setPeriodicBoxVectors( old_solvated_topology.getPeriodicBoxVectors()) #create the new ligand system: new_solvated_system = self._system_generator.build_system( new_solvated_ligand_omm_topology) new_to_old_atom_map = { complex_atom_map[x] - new_mol_start_index: x - old_mol_start_index for x in old_complex.select("resname == 'MOL' ") if x in complex_atom_map.keys() } #adjust the atom map to account for the presence of solvent degrees of freedom: #By design, all atoms after the ligands are water, and should be mapped. n_water_atoms = solvent_only_topology.to_openmm().getNumAtoms() for i in range(n_water_atoms): new_to_old_atom_map[new_mol_len + i] = old_mol_len + i #change the map to accomodate the TP: #new_to_old_atom_map = {value : key for key, value in atom_map_adjusted.items()} #make a TopologyProposal ligand_topology_proposal = TopologyProposal( new_topology=new_solvated_ligand_omm_topology, new_system=new_solvated_system, old_topology=old_solvated_topology, old_system=old_solvated_system, new_to_old_atom_map=new_to_old_atom_map, old_chemical_state_key='A', new_chemical_state_key='B') return ligand_topology_proposal, old_solvated_positions @property def complex_topology_proposal(self): return self._complex_topology_proposal @property def complex_old_positions(self): return self._complex_positions_old_solvated @property def complex_new_positions(self): return self._complex_positions_new_solvated @property def solvent_topology_proposal(self): return self._solvent_topology_proposal @property def solvent_old_positions(self): return self._old_solvent_positions @property def solvent_new_positions(self): return self._new_solvent_positions
def generate_vacuum_topology_proposal(current_mol_name="benzene", proposed_mol_name="toluene", forcefield_kwargs=None, system_generator_kwargs=None): """ Generate a test vacuum topology proposal, current positions, and new positions triplet from two IUPAC molecule names. Constraints are added to the system by default. To override this, set ``forcefield_kwargs = None``. Parameters ---------- current_mol_name : str, optional name of the first molecule proposed_mol_name : str, optional name of the second molecule forcefield_kwargs : dict, optional, default=None Additional arguments to ForceField in addition to 'removeCMMotion': False, 'nonbondedMethod': app.NoCutoff system_generator_kwargs : dict, optional, default=None Dict passed onto SystemGenerator Returns ------- topology_proposal : perses.rjmc.topology_proposal The topology proposal representing the transformation current_positions : np.array, unit-bearing The positions of the initial system new_positions : np.array, unit-bearing The positions of the new system """ from openmoltools import forcefield_generators from perses.tests.utils import createOEMolFromIUPAC, createSystemFromIUPAC, get_data_filename gaff_filename = get_data_filename('data/gaff.xml') default_forcefield_kwargs = {'removeCMMotion': False, 'nonbondedMethod': app.NoCutoff, 'constraints' : app.HBonds} forcefield_kwargs = default_forcefield_kwargs.update(forcefield_kwargs) if (forcefield_kwargs is not None) else default_forcefield_kwargs system_generator_kwargs = system_generator_kwargs if (system_generator_kwargs is not None) else dict() system_generator = SystemGenerator([gaff_filename, 'amber99sbildn.xml', 'tip3p.xml'], forcefield_kwargs=forcefield_kwargs, **system_generator_kwargs) old_oemol = createOEMolFromIUPAC(current_mol_name) from openmoltools.forcefield_generators import generateTopologyFromOEMol old_topology = generateTopologyFromOEMol(old_oemol) old_positions = extractPositionsFromOEMOL(old_oemol) old_smiles = oechem.OEMolToSmiles(old_oemol) old_system = system_generator.build_system(old_topology) new_oemol = createOEMolFromIUPAC(proposed_mol_name) new_smiles = oechem.OEMolToSmiles(new_oemol) geometry_engine = geometry.FFAllAngleGeometryEngine() proposal_engine = SmallMoleculeSetProposalEngine( [old_smiles, new_smiles], system_generator, residue_name=current_mol_name) #generate topology proposal topology_proposal = proposal_engine.propose(old_system, old_topology, current_mol=old_oemol, proposed_mol=new_oemol) # show atom mapping filename = str(current_mol_name)+str(proposed_mol_name)+'.pdf' render_atom_mapping(filename, old_oemol, new_oemol, topology_proposal.new_to_old_atom_map) #generate new positions with geometry engine new_positions, _ = geometry_engine.propose(topology_proposal, old_positions, beta) # DEBUG: Zero out bonds and angles for one system #print('Zeroing bonds of old system') #for force in topology_proposal.old_system.getForces(): # if force.__class__.__name__ == 'HarmonicAngleForce': # for index in range(force.getNumAngles()): # p1, p2, p3, angle, K = force.getAngleParameters(index) # K *= 0.0 # force.setAngleParameters(index, p1, p2, p3, angle, K) # if False and force.__class__.__name__ == 'HarmonicBondForce': # for index in range(force.getNumBonds()): # p1, p2, r0, K = force.getBondParameters(index) # K *= 0.0 # force.setBondParameters(index, p1, p2, r0, K) # DEBUG : Box vectors #box_vectors = np.eye(3) * 100 * unit.nanometers #topology_proposal.old_system.setDefaultPeriodicBoxVectors(box_vectors[0,:], box_vectors[1,:], box_vectors[2,:]) #topology_proposal.new_system.setDefaultPeriodicBoxVectors(box_vectors[0,:], box_vectors[1,:], box_vectors[2,:]) return topology_proposal, old_positions, new_positions
initial_mol = mol_list[initial_ligand] proposal_mol = mol_list[proposal_ligand] proposal_smiles = SmallMoleculeSetProposalEngine.canonicalize_smiles( oechem.OECreateCanSmiString(proposal_mol)) current_smiles = SmallMoleculeSetProposalEngine.canonicalize_smiles( oechem.OECreateCanSmiString(initial_mol)) barostat = openmm.MonteCarloBarostat(1.0 * unit.atmosphere, temperature, 50) system_generator = SystemGenerator( [ 'amber14/protein.ff14SB.xml', 'gaff.xml', 'amber14/tip3p.xml', 'MCL1_ligands.xml' ], barostat=barostat, forcefield_kwargs={ 'nonbondedMethod': app.PME, 'constraints': app.HBonds, 'hydrogenMass': 4 * unit.amus }, use_antechamber=False) atom_mapper_filename = os.path.join( setup_directory, "{}_atom_mapper.json".format(project_prefix)) with open(atom_mapper_filename, 'r') as infile: atom_mapper = SmallMoleculeAtomMapper.from_json(infile.read()) proposal_engine = PremappedSmallMoleculeSetProposalEngine( atom_mapper, system_generator) topology_proposal = proposal_engine.propose(system,
def create_systems(topologies_dict, positions_dict, output_directory, project_prefix, solvate=True): """ Generate the systems ready for equilibrium simulations from a dictionary of topologies and positions Parameters ---------- topologies_dict : dict of str: app.Topoology A dictionary of the topologies to prepare, indexed by SMILES strings positions_dict : dict of str: unit.Quantity array A dictionary of positions for the corresponding topologies, indexed by SMILES strings output_directory : str Location of output files project_prefix : str What to prepend to the names of files for this run solvate : bool, default True Whether to solvate the systems """ barostat = openmm.MonteCarloBarostat(1.0*unit.atmosphere, temperature, 50) system_generator = SystemGenerator(['amber14/protein.ff14SB.xml', 'gaff.xml', 'amber14/tip3p.xml', 'MCL1_ligands.xml'], barostat=barostat, forcefield_kwargs={'nonbondedMethod': app.PME, 'constraints': app.HBonds, 'hydrogenMass': 4 * unit.amus}, use_antechamber=False) list_of_smiles = list(topologies_dict.keys()) initial_smiles = list_of_smiles[0] initial_topology = topologies_dict[initial_smiles] initial_positions = positions_dict[initial_smiles] if solvate: solvated_initial_positions, solvated_topology, solvated_system = solvate_system(initial_topology.to_openmm(), initial_positions, system_generator) else: solvated_initial_positions = initial_positions solvated_topology = initial_topology solvated_system = system_generator.build_system(solvated_topology) md_topology = md.Topology.from_openmm(solvated_topology) if solvate: num_added = md_topology.n_residues - initial_topology.n_residues if not os.path.exists(output_directory): os.mkdir(output_directory) np.save("{}/{}_{}_initial.npy".format(output_directory,project_prefix, 0), (solvated_initial_positions, md_topology, solvated_system, initial_smiles)) for i in tqdm.trange(1, len(list_of_smiles)): smiles = list_of_smiles[i] topology = topologies_dict[smiles] positions = positions_dict[smiles] if solvate: solvated_positions, solvated_topology, solvated_system = solvate_system(topology.to_openmm(), positions, system_generator, padding=None, num_added=num_added) else: solvated_positions = initial_positions solvated_topology = initial_topology solvated_system = system_generator.build_system(solvated_topology) np.save("{}/{}_{}_initial.npy".format(output_directory,project_prefix, i), (solvated_positions, md.Topology.from_openmm(solvated_topology), solvated_system, smiles))