def getMolParamIDToAtomIndex(molecule, forcefield): """Take a Molecule and a SMIRNOFF forcefield object and return a dictionary, keyed by parameter ID, where each entry is a tuple of ( smirks, [[atom1, ... atomN], [atom1, ... atomN]) giving the SMIRKS corresponding to that parameter ID and a list of the atom groups in that molecule that parameter is applied to. Parameters ---------- molecule : openforcefield.topology.Molecule Molecule to investigate forcefield : ForceField SMIRNOFF ForceField object (obtained from an ffxml via ForceField(ffxml)) containing FF of interest. Returns ------- param_usage : dictionary Dictionary, keyed by parameter ID, where each entry is a tuple of ( smirks, [[atom1, ... atomN], [atom1, ... atomN]) giving the SMIRKS corresponding to that parameter ID and a list of the atom groups in that molecule that parameter is applied to. """ topology = Topology() topology.add_molecule(molecule) labels = ff.labal_molecules(topology) param_usage = {} for mol_entry in range(len(labels)): for force in labels[mol_entry].keys(): for (atom_indices, pid, smirks) in labels[mol_entry][force]: if not pid in param_usage: param_usage[pid] = (smirks, [atom_indices]) else: param_usage[pid][1].append( atom_indices ) return param_usage
def test_get_virtual_site(self): """Test Topology.virtual_site function (get virtual site from index)""" topology = Topology() topology.add_molecule(self.ethane_from_smiles_w_vsites) assert topology.n_topology_virtual_sites == 2 topology.add_molecule(self.propane_from_smiles_w_vsites) assert topology.n_topology_virtual_sites == 4 with self.assertRaises(Exception) as context: topology_vsite = topology.virtual_site(-1) with self.assertRaises(Exception) as context: topology_vsite = topology.virtual_site(4) topology_vsite1 = topology.virtual_site(0) topology_vsite2 = topology.virtual_site(1) topology_vsite3 = topology.virtual_site(2) topology_vsite4 = topology.virtual_site(3) assert topology_vsite1.type == "BondChargeVirtualSite" assert topology_vsite2.type == "MonovalentLonePairVirtualSite" assert topology_vsite3.type == "BondChargeVirtualSite" assert topology_vsite4.type == "MonovalentLonePairVirtualSite" n_equal_atoms = 0 for topology_atom in topology.topology_atoms: for vsite in topology.topology_virtual_sites: for vsite_atom in vsite.atoms: if topology_atom == vsite_atom: n_equal_atoms += 1 # There are four virtual sites -- Two BondCharges with 2 atoms, and two MonovalentLonePairs with 3 atoms assert n_equal_atoms == 10
def test_n_topology_atoms(self): """Test n_atoms function""" topology = Topology() assert topology.n_topology_atoms == 0 assert topology.n_topology_bonds == 0 topology.add_molecule(self.ethane_from_smiles) assert topology.n_topology_atoms == 8 assert topology.n_topology_bonds == 7
def test_to_from_openmm(self): """Test a round-trip OpenFF -> OpenMM -> OpenFF Topology.""" from simtk.openmm.app import Aromatic # Create OpenFF topology with 1 ethanol and 2 benzenes. ethanol = Molecule.from_smiles("CCO") benzene = Molecule.from_smiles("c1ccccc1") off_topology = Topology.from_molecules( molecules=[ethanol, benzene, benzene]) # Convert to OpenMM Topology. omm_topology = off_topology.to_openmm() # Check that bond orders are preserved. n_double_bonds = sum([b.order == 2 for b in omm_topology.bonds()]) n_aromatic_bonds = sum( [b.type is Aromatic for b in omm_topology.bonds()]) assert n_double_bonds == 6 assert n_aromatic_bonds == 12 # Check that there is one residue for each molecule. assert omm_topology.getNumResidues() == 3 assert omm_topology.getNumChains() == 3 # Convert back to OpenFF Topology. off_topology_copy = Topology.from_openmm( omm_topology, unique_molecules=[ethanol, benzene]) # The round-trip OpenFF Topology is identical to the original. # The reference molecules are the same. assert (off_topology.n_reference_molecules == off_topology_copy.n_reference_molecules) reference_molecules_copy = list(off_topology_copy.reference_molecules) for ref_mol_idx, ref_mol in enumerate( off_topology.reference_molecules): assert ref_mol == reference_molecules_copy[ref_mol_idx] # The number of topology molecules is the same. assert (off_topology.n_topology_molecules == off_topology_copy.n_topology_molecules) # Check atoms. assert off_topology.n_topology_atoms == off_topology_copy.n_topology_atoms for atom_idx, atom in enumerate(off_topology.topology_atoms): atom_copy = off_topology_copy.atom(atom_idx) assert atom.atomic_number == atom_copy.atomic_number # Check bonds. for bond_idx, bond in enumerate(off_topology.topology_bonds): bond_copy = off_topology_copy.bond(bond_idx) bond_atoms = [a.atomic_number for a in bond.atoms] bond_atoms_copy = [a.atomic_number for a in bond_copy.atoms] assert bond_atoms == bond_atoms_copy assert bond.bond_order == bond_copy.bond_order assert bond.bond.is_aromatic == bond_copy.bond.is_aromatic
def test_to_file_units_check(self): """ Checks whether writing pdb with unitless positions, Angstrom positions, nanometer positions, result in the same output """ import filecmp from tempfile import NamedTemporaryFile from simtk.unit import nanometer from openforcefield.tests.test_forcefield import create_ethanol from openforcefield.topology import Molecule, Topology topology = Topology() mol = Molecule.from_pdb_and_smiles( get_data_file_path("systems/test_systems/1_ethanol.pdb"), "CCO") topology.add_molecule(mol) positions_angstrom = mol.conformers[0] count = 1 # Write the molecule to PDB and ensure that the X coordinate of the first atom is 10.172 with NamedTemporaryFile(suffix=".pdb") as iofile: topology.to_file(iofile.name, positions_angstrom) data = open(iofile.name).readlines() for line in data: if line.startswith("HETATM") and count == 1: count = count + 1 coord = line.split()[-6] assert coord == "10.172" # Do the same check, but feed in equivalent positions measured in nanometers and ensure the PDB is still the same count = 1 coord = None with NamedTemporaryFile(suffix=".pdb") as iofile: positions_nanometer = positions_angstrom.in_units_of(nanometer) topology.to_file(iofile.name, positions_nanometer) data = open(iofile.name).readlines() for line in data: if line.startswith("HETATM") and count == 1: count = count + 1 coord = line.split()[-6] assert coord == "10.172" count = 1 coord = "abc" with NamedTemporaryFile(suffix=".pdb") as iofile: positions_unitless = positions_angstrom._value topology.to_file(iofile.name, positions_unitless) data = open(iofile.name).readlines() for line in data: if line.startswith("HETATM") and count == 1: count = count + 1 coord = line.split()[-6] assert coord == "10.172"
def generateTopologyFromOEMol(molecule): """ Generate an OpenMM Topology object from an OEMol molecule. Parameters ---------- molecule : openeye.oechem.OEMol The molecule from which a Topology object is to be generated. Returns ------- topology : simtk.openmm.app.Topology The Topology object generated from `molecule`. """ warnings.warn(DEPRECATION_WARNING_TEXT, PendingDeprecationWarning) from openeye import oechem # Avoid manipulating the molecule mol = oechem.OEMol(molecule) # Create a Topology object with one Chain and one Residue. from simtk.openmm.app import Topology topology = Topology() chain = topology.addChain() resname = mol.GetTitle() residue = topology.addResidue(resname, chain) # Make sure the atoms have names, otherwise bonds won't be created properly below if any([atom.GetName() == '' for atom in mol.GetAtoms()]): oechem.OETriposAtomNames(mol) # Check names are unique; non-unique names will also cause a problem atomnames = [atom.GetName() for atom in mol.GetAtoms()] if any(atomnames.count(atom.GetName()) > 1 for atom in mol.GetAtoms()): raise Exception( "Error: Reference molecule must have unique atom names in order to create a Topology." ) # Create atoms in the residue. for atom in mol.GetAtoms(): name = atom.GetName() element = openmm.app.element.Element.getByAtomicNumber( atom.GetAtomicNum()) topology.addAtom(name, element, residue) # Create bonds. atoms = {atom.name: atom for atom in topology.atoms()} for bond in mol.GetBonds(): aromatic = None if bond.IsAromatic(): aromatic = 'Aromatic' # Add bond, preserving order assessed by OEChem topology.addBond(atoms[bond.GetBgn().GetName()], atoms[bond.GetEnd().GetName()], type=aromatic, order=bond.GetOrder()) return topology
def test_box_vectors(self): """Test the getter and setter for box_vectors""" topology = Topology() good_box_vectors = unit.Quantity(np.array([10, 20, 30]), unit.angstrom) bad_box_vectors = np.array([10, 20, 30 ]) # They're bad because they're unitless assert topology.box_vectors is None with self.assertRaises(ValueError) as context: topology.box_vectors = bad_box_vectors assert topology.box_vectors is None topology.box_vectors = good_box_vectors assert (topology.box_vectors == good_box_vectors).all()
def smirnoff_analyze_torsions(forcefield,off_mol): # Compute the coverage of all torsions in this molecule torsions_coverage = defaultdict(list) off_top = Off_Topology.from_molecules(off_mol) for torsion_indices, torsion_param in forcefield.label_molecules(off_top)[0]['ProperTorsions'].items(): torsions_coverage[torsion_param].append(torsion_indices) return torsions_coverage
def min_ffxml(mol, ffxml): # make copy of the input mol oe_mol = oechem.OEGraphMol(mol) try: # create openforcefield molecule ==> prone to triggering Exception off_mol = Molecule.from_openeye(oe_mol) # load in force field ff = ForceField(ffxml) # create components for OpenMM system topology = Topology.from_molecules(molecules=[off_mol]) # create openmm system ==> prone to triggering Exception #system = ff.create_openmm_system(topology, charge_from_molecules=[off_mol]) system = ff.create_openmm_system(topology) except Exception as e: smilabel = oechem.OEGetSDData(oe_mol, "SMILES QCArchive") print( ' >>> openforcefield failed to create OpenMM system: ' f"'{oe_mol.GetTitle()}' '{smilabel}'") print(f"{e}\n") return print(" >>> successful OpenMM system creation for openforcefield " f"mol \"{oe_mol.GetTitle()}\"")
def test_chemical_environments_matches_RDK(self): """Test Topology.chemical_environment_matches""" from simtk.openmm import app toolkit_wrapper = RDKitToolkitWrapper() pdbfile = app.PDBFile( get_data_file_path( "systems/packmol_boxes/cyclohexane_ethanol_0.4_0.6.pdb")) # toolkit_wrapper = RDKitToolkitWrapper() # molecules = [Molecule.from_file(get_data_file_path(name)) for name in ('molecules/ethanol.mol2', # 'molecules/cyclohexane.mol2')] molecules = [] molecules.append(Molecule.from_smiles("CCO")) molecules.append(Molecule.from_smiles("C1CCCCC1")) topology = Topology.from_openmm(pdbfile.topology, unique_molecules=molecules) # Count CCO matches matches = topology.chemical_environment_matches( "[C:1]-[C:2]-[O:3]", toolkit_registry=toolkit_wrapper) assert len(matches) == 143 assert matches[0].topology_atom_indices == (1728, 1729, 1730) matches = topology.chemical_environment_matches( "[H][C:1]([H])([H])-[C:2]([H])([H])-[O:3][H]", toolkit_registry=toolkit_wrapper, ) assert (len(matches) == 1716 ) # 143 * 12 (there are 12 possible hydrogen mappings) assert matches[0].topology_atom_indices == (1728, 1729, 1730) # Search for a substructure that isn't there matches = topology.chemical_environment_matches( "[C][C:1]-[C:2]-[O:3]", toolkit_registry=toolkit_wrapper) assert len(matches) == 0
def data_generator(): for record_name in random.sample(list(ds_qc.data.records), 10): try: print(record_name, flush=True) r = ds_qc.get_record(record_name, specification='default') if r is not None: traj = r.get_trajectory() if traj is not None: for snapshot in traj: mol = snapshot.get_molecule() # mol = snapshot.get_molecule().dict(encoding='json') xyz = tf.convert_to_tensor( mol.geometry * BOHR_TO_NM, dtype=tf.float32) qm_force = tf.convert_to_tensor( snapshot.return_result\ * HARTREE_PER_BOHR_TO_KJ_PER_MOL_PER_NM, dtype=tf.float32) mol = cmiles.utils.load_molecule(mol.dict(encoding='json')) top = Topology.from_molecules(Molecule.from_openeye(mol)) sys = FF.create_openmm_system(top) yield( xyz, qm_force, sys) except: pass
def test_to_file_no_molecules(self): """ Checks if Topology.to_file() writes a file with no topology and no coordinates """ from tempfile import NamedTemporaryFile from openforcefield.topology import Topology topology = Topology() lines = [] with NamedTemporaryFile(suffix=".pdb") as iofile: topology.to_file(iofile.name, []) data = open(iofile.name).readlines() for line in data: lines.append(line.split()) assert lines[1] == ["END"]
def test_chemical_environments_matches_OE(self): """Test Topology.chemical_environment_matches""" from simtk.openmm import app toolkit_wrapper = OpenEyeToolkitWrapper() pdbfile = app.PDBFile( get_data_file_path( 'systems/packmol_boxes/cyclohexane_ethanol_0.4_0.6.pdb')) # toolkit_wrapper = RDKitToolkitWrapper() molecules = [ Molecule.from_file(get_data_file_path(name)) for name in ('molecules/ethanol.mol2', 'molecules/cyclohexane.mol2') ] topology = Topology.from_openmm(pdbfile.topology, unique_molecules=molecules) # Test for substructure match matches = topology.chemical_environment_matches( "[C:1]-[C:2]-[O:3]", toolkit_registry=toolkit_wrapper) assert len(matches) == 143 assert tuple(i.topology_atom_index for i in matches[0]) == (1728, 1729, 1730) # Test for whole-molecule match matches = topology.chemical_environment_matches( "[H][C:1]([H])([H])-[C:2]([H])([H])-[O:3][H]", toolkit_registry=toolkit_wrapper) assert len( matches ) == 1716 # 143 * 12 (there are 12 possible hydrogen mappings) assert tuple(i.topology_atom_index for i in matches[0]) == (1728, 1729, 1730) # Search for a substructure that isn't there matches = topology.chemical_environment_matches( "[C][C:1]-[C:2]-[O:3]", toolkit_registry=toolkit_wrapper) assert len(matches) == 0
def serialise_system(self): """Create the OpenMM system; parametrise using frost; serialise the system.""" # Load the molecule using openforcefield pdb_file = app.PDBFile(f'{self.molecule.name}.pdb') # Now we need the connection info try using smiles string from rdkit rdkit = RDKit() molecule = Molecule.from_smiles( rdkit.get_smiles(f'{self.molecule.name}.pdb')) # Make the openMM system omm_topology = pdb_file.topology off_topology = Topology.from_openmm(omm_topology, unique_molecules=[molecule]) # Load the smirnoff99Frosst force field. forcefield = ForceField('test_forcefields/smirnoff99Frosst.offxml') # Parametrize the topology and create an OpenMM System. system = forcefield.create_openmm_system(off_topology) # Serialise the OpenMM system into the xml file with open('serialised.xml', 'w+') as out: out.write(XmlSerializer.serializeSystem(system))
def test_to_openmm_assign_unique_atom_names_some_duplicates(self): """ Ensure that OFF topologies where some molecules have invalid/duplicate atom names have unique atom names applied while the other molecules are unaffected. """ # Create OpenFF topology with 1 ethanol and 2 benzenes. ethanol = Molecule.from_smiles("CCO") # Assign duplicate atom names in ethanol (two AT0s) ethanol_atom_names_with_duplicates = [ f"AT{i}" for i in range(ethanol.n_atoms) ] ethanol_atom_names_with_duplicates[1] = "AT0" for atom, atom_name in zip(ethanol.atoms, ethanol_atom_names_with_duplicates): atom.name = atom_name # Assign unique atom names in benzene benzene = Molecule.from_smiles("c1ccccc1") benzene_atom_names = [f"AT{i}" for i in range(benzene.n_atoms)] for atom, atom_name in zip(benzene.atoms, benzene_atom_names): atom.name = atom_name off_topology = Topology.from_molecules( molecules=[ethanol, benzene, benzene]) omm_topology = off_topology.to_openmm() atom_names = set() for atom in omm_topology.atoms(): atom_names.add(atom.name) # There should be 12 "AT#"-labeled atoms (from benzene), 2 unique Cs, # 1 unique O, and 6 unique Hs, for a total of 21 unique atom names assert len(atom_names) == 21
def openff(self): # Load the molecule (for now mol2, until charges are saved on sdf) molecule = Molecule.from_file(self.lig + '.mol2') topology = Topology.from_molecules([molecule]) # Label using the smirnoff99Frosst force field self.forcefield = ForceField('smirnoff99Frosst.offxml') self.parameters = self.forcefield.label_molecules(topology)[0]
def checkTorsion(smiles, torsion_indices, ff_name): """ Take mollist and check if the molecules in a list match a specific torsion id Parameters ---------- molList : List of objects List of oemols with datatags generated in genData function Returns ------- molList : list of objects List of oemol objects that have a datatag "IDMatch" that contain the torsion id involved in the QCA torsion drive """ matches = [] count = 0 mols = [] #tid='' #molecule = Molecule.from_mapped_smiles(smiles) print(smiles) from openeye import oechem # create a new molecule #mol = oechem.OEGraphMol() # convert the SMILES string into a molecule #oechem.OESmilesToMol(mol,smiles) #molecule = Molecule.from_smiles(smiles) #molecule=Molecule.from_openeye(mol) molecule = Molecule.from_mapped_smiles(smiles) topology = Topology.from_molecules(molecule) # Let's label using the Parsley force field forcefield = ForceField(ff_name, allow_cosmetic_attributes=True) # Run the molecule labeling molecule_force_list = forcefield.label_molecules(topology) params = [] indices = [] # Print out a formatted description of the torsion parameters applied to this molecule for mol_idx, mol_forces in enumerate(molecule_force_list): # print(f'Forces for molecule {mol_idx}') for force_tag, force_dict in mol_forces.items(): if force_tag == "ProperTorsions": for (atom_indices, parameter) in force_dict.items(): params.append(parameter.id) indices.append(atom_indices) #torsion_indices=tuple(torsion_indices) #print(type(torsion_indices)) print(torsion_indices) #print(type(atom_indices)) print(atom_indices) if atom_indices == torsion_indices or tuple( reversed(atom_indices)) == torsion_indices: #mol.SetData("IDMatch", parameter.id) tid = parameter.id print(params) print(indices) return tid
def get_assigned_torsion_param(tdentry, forcefield): """Get the OpenFF forcefield torsion parameter ultimately assigned to the given TorsionDrive entry's torsion dihedral. Parameters ---------- tdentry : TDEntry TDEntry (TorsionDrive entry) to operate on; will be used to generate molecule, extract dihedral indices driven. forcefield : str, ForceField OpenFF forcefield to apply. Returns ------- torsion_params : ProperTorsion Dict-like object with attributes giving the applied torsion parameters Examples -------- Starting with TDEntries from usage of `get_torsiondrives_matching_smarts` (see its Example), we can get back the parameter assigned to this by, say `"openff-1.0.0.offxml"`: >>> from openforcefield.typing.engines.smirnoff import ForceField >>> tdentries = get_torsiondrives_matching_smarts(smarts, dataset, client) >>> ff = ForceField('openff-1.0.0.offxml') >>> assigned = [smarts_torsions.get_assigned_torsion_param(tdentry, ff) for tdentry in tdentries] >>> print([t.id for t in assigned]) ['t47', 't47', 't47', 't47', ...] """ mol_smiles = tdentry.attributes["canonical_isomeric_explicit_hydrogen_mapped_smiles"] offmol = Molecule.from_mapped_smiles(mol_smiles) if isinstance(forcefield, str): forcefield = ForceField(forcefield) # apply forcefield parameters topology = Topology.from_molecules(offmol) # we only have one molecule by definition here, so extracting 0th molecule_forces = forcefield.label_molecules(topology)[0] # by convention, we only have one driven torsion # would need to revisit if we are working with 2D torsions dihedral_indices = tdentry.td_keywords.dihedrals[0] # get torsion parameters corresponding to dihedral indices torsions = molecule_forces["ProperTorsions"] torsion_params = torsions.get(dihedral_indices) # if None, try reversing it if torsion_params is None: torsion_params = torsions[dihedral_indices[::-1]] return torsion_params
def minimise_energy_all_confs(mol, models = None, epsilon = 4, allow_undefined_stereo = True, **kwargs ): from simtk import unit from simtk.openmm import LangevinIntegrator from simtk.openmm.app import Simulation, HBonds, NoCutoff from rdkit import Chem from rdkit.Geometry import Point3D import mlddec import copy import tqdm mol = Chem.AddHs(mol, addCoords = True) if models is None: models = mlddec.load_models(epsilon) charges = mlddec.get_charges(mol, models) from openforcefield.utils.toolkits import RDKitToolkitWrapper, ToolkitRegistry from openforcefield.topology import Molecule, Topology from openforcefield.typing.engines.smirnoff import ForceField # from openforcefield.typing.engines.smirnoff.forcefield import PME import parmed import numpy as np forcefield = ForceField(get_data_filename("modified_smirnoff99Frosst.offxml")) #FIXME better way of identifying file location tmp = copy.deepcopy(mol) tmp.RemoveAllConformers() #XXX workround for speed beacuse seemingly openforcefield records all conformer informations, which takes a long time. but I think this is a ill-practice molecule = Molecule.from_rdkit(tmp, allow_undefined_stereo = allow_undefined_stereo) molecule.partial_charges = unit.Quantity(np.array(charges), unit.elementary_charge) topology = Topology.from_molecules(molecule) openmm_system = forcefield.create_openmm_system(topology, charge_from_molecules= [molecule]) structure = parmed.openmm.topsystem.load_topology(topology.to_openmm(), openmm_system) system = structure.createSystem(nonbondedMethod=NoCutoff, nonbondedCutoff=1*unit.nanometer, constraints=HBonds) integrator = LangevinIntegrator(273*unit.kelvin, 1/unit.picosecond, 0.002*unit.picoseconds) simulation = Simulation(structure.topology, system, integrator) out_mol = copy.deepcopy(mol) for i in tqdm.tqdm(range(out_mol.GetNumConformers())): conf = mol.GetConformer(i) structure.coordinates = unit.Quantity(np.array([np.array(conf.GetAtomPosition(i)) for i in range(mol.GetNumAtoms())]), unit.angstroms) simulation.context.setPositions(structure.positions) simulation.minimizeEnergy() # simulation.step(1) coords = simulation.context.getState(getPositions = True).getPositions(asNumpy = True).value_in_unit(unit.angstrom) conf = out_mol.GetConformer(i) for j in range(out_mol.GetNumAtoms()): conf.SetAtomPosition(j, Point3D(*coords[j])) return out_mol
def paramUsage(smilesList, offxml): """ Description - Reads in list of smiles and returns a dictionary of .offxml style parameters as keys and smiles of molecules as items Input - smilesList: A list of smiles offxml: The .offxml format force field that the parameters will be used with Return - anglebondDict: A dictionary of .offxml style parameters as keys and smiles of molecules that utilize parameters. The returned dictionary is only for bond and angle parameters e.g. 'a1', 'b2', etc. Note: The function can be modified to return a dictionary of torsion parameters. """ # Initialize storage torsionDict = dict() anglebondDict = dict() # Let's label using our RC force field forcefield = ForceField(offxml) # Loop over smiles for smi in smilesList: # Create a simple molecule from SMILES and turn it into a topology. molecule = Molecule.from_smiles(smi, allow_undefined_stereo = True) topology = Topology.from_molecules([molecule]) # Run the molecule labeling molecule_force_list = forcefield.label_molecules(topology) # Print out a formatted description of the parameters applied to this molecule for mol_idx, mol_forces in enumerate(molecule_force_list): for force_tag, force_dict in mol_forces.items(): for (atom_indices, parameter) in force_dict.items(): pid = parameter.id #create two seperate parameter usage dictionaries for (1) angle and bonds and (2) torsions if "a" in pid or "b" in pid: if not pid in anglebondDict: anglebondDict[pid] = set() anglebondDict[pid].add(smi) #Uncomment this for torsion dictionary #if "t" in pid: # if not pid in torsionDict: # torsionDict[pid] = set() # torsionDict[pid].add(smi) #Write out the angle and bond dictionary to "anglebond.p" file pickle.dump(anglebondDict, open( "anglebond.p", "wb" ) ) return anglebondDict
def _execute(self, directory, available_resources): from openforcefield.topology import Molecule, Topology pdb_file = app.PDBFile(self.coordinate_file_path) force_field_source = ForceFieldSource.from_json(self.force_field_path) if not isinstance(force_field_source, SmirnoffForceFieldSource): raise ValueError( "Only SMIRNOFF force fields are supported by this protocol.") force_field = force_field_source.to_force_field() unique_molecules = [] charged_molecules = [] if self.apply_known_charges: charged_molecules = self._generate_known_charged_molecules() # Load in any additional, user specified charged molecules. for charged_molecule_path in self.charged_molecule_paths: charged_molecule = Molecule.from_file(charged_molecule_path, "MOL2") charged_molecules.append(charged_molecule) for component in self.substance.components: molecule = Molecule.from_smiles(smiles=component.smiles) if molecule is None: raise ValueError( f"{component} could not be converted to a Molecule") unique_molecules.append(molecule) topology = Topology.from_openmm(pdb_file.topology, unique_molecules=unique_molecules) if len(charged_molecules) > 0: system = force_field.create_openmm_system( topology, charge_from_molecules=charged_molecules) else: system = force_field.create_openmm_system(topology) if system is None: raise RuntimeError( "Failed to create a system from the specified topology and molecules." ) system_xml = openmm.XmlSerializer.serialize(system) self.system_path = os.path.join(directory, "system.xml") with open(self.system_path, "w") as file: file.write(system_xml)
def make_off_system(mol, ID): ##Now over to OpenForceField: drug_pdbfile = PDBFile('./processed_data/aligned_drugs/drug_' + str(ID) + '.pdb') drug_mol = Molecule.from_smiles(Chem.MolToSmiles(mol)) off_topology = Topology.from_openmm(openmm_topology=drug_pdbfile.topology, unique_molecules=[drug_mol]) #actual parameterizing step: drug_system = ff.create_openmm_system(off_topology) return drug_system
def test_empty(self): """Test creation of empty topology""" topology = Topology() assert topology.n_reference_molecules == 0 assert topology.n_topology_molecules == 0 assert topology.n_topology_atoms == 0 assert topology.n_topology_bonds == 0 assert topology.n_topology_particles == 0 assert topology.n_topology_virtual_sites == 0 assert topology.box_vectors is None assert len(topology.constrained_atom_pairs.items()) == 0
def create_openmm_system(conversion, molecules): """ Create an OpenMM system using the input MOL2 file and force field file. """ molecule = Molecule.from_openeye(molecules[0]) topology = Topology.from_molecules([molecule]) ff = ForceField(conversion.ff) system = ff.create_openmm_system(topology) return topology, system
def test_to_file_multi_molecule_different_order(self): """ Checks for the following if Topology.to_write maintains the order of atoms for the same molecule with different indexing """ from tempfile import NamedTemporaryFile from openforcefield.tests.test_forcefield import ( create_ethanol, create_reversed_ethanol, ) from openforcefield.topology import Molecule, Topology topology = Topology() topology.add_molecule(create_ethanol()) topology.add_molecule(create_reversed_ethanol()) mol = Molecule.from_pdb_and_smiles( get_data_file_path("systems/test_systems/1_ethanol.pdb"), "CCO") positions = mol.conformers[0] # Make up coordinates for the second ethanol by translating the first by 10 angstroms # (note that this will still be a gibberish conformation, since the atom order in the second molecule is different) positions = np.concatenate( [positions, positions + 10.0 * unit.angstrom]) element_order = [] with NamedTemporaryFile(suffix=".pdb") as iofile: topology.to_file(iofile.name, positions) data = open(iofile.name).readlines() for line in data: if line.startswith("HETATM"): element_order.append(line.strip()[-1]) assert element_order == [ "C", "C", "O", "H", "H", "H", "H", "H", "H", "H", "H", "H", "H", "H", "H", "O", "C", "C", ]
def test_to_file_vsites(self): """ Checks that Topology.to_file() doesn't write vsites """ from tempfile import NamedTemporaryFile from openforcefield.topology import Molecule, Topology mol = Molecule.from_pdb_and_smiles( get_data_file_path("systems/test_systems/1_ethanol.pdb"), "CCO") carbons = [atom for atom in mol.atoms if atom.atomic_number == 6] positions = mol.conformers[0] mol.add_bond_charge_virtual_site( (carbons[0], carbons[1]), 0.1 * unit.angstrom, charge_increments=[0.1, 0.05] * unit.elementary_charge, ) topology = Topology() topology.add_molecule(mol) count = 0 # The file should be printed out with 9 atoms and 0 virtualsites, so we check to ensure that thtere are only 9 HETATM entries with NamedTemporaryFile(suffix=".pdb") as iofile: topology.to_file(iofile.name, positions) data = open(iofile.name).readlines() for line in data: if line.startswith("HETATM"): count = count + 1 assert count == 9
def test_topology_virtualsites_atom_indexing(self): """ Add multiple instances of the same molecule, but in a different order, and ensure that virtualsite atoms are indexed correctly """ topology = Topology() topology.add_molecule(create_ethanol()) topology.add_molecule(create_ethanol()) topology.add_molecule(create_reversed_ethanol()) # Add a virtualsite to the reference ethanol for ref_mol in topology.reference_molecules: ref_mol._add_bond_charge_virtual_site( [0, 1], 0.5 * unit.angstrom, ) virtual_site_topology_atom_indices = [(0, 1), (9, 10), (26, 25)] for top_vs, expected_indices in zip( topology.topology_virtual_sites, virtual_site_topology_atom_indices): assert (tuple([at.topology_particle_index for at in top_vs.atoms]) == expected_indices) assert top_vs.atom( 0).topology_particle_index == expected_indices[0] assert top_vs.atom( 1).topology_particle_index == expected_indices[1]
def _create_impropers_only_system( smiles: str = "CC1=C(C(=O)C2=C(C1=O)N3CC4C(C3(C2COC(=O)N)OC)N4)N", ) -> mm.System: """Create a simulation that contains only improper torsion terms, by parameterizing with openff-1.2.0 and deleting all terms but impropers """ molecule = Molecule.from_smiles(smiles, allow_undefined_stereo=True) g = esp.Graph(molecule) topology = Topology.from_molecules(molecule) forcefield = ForceField("openff-1.2.0.offxml") openmm_system = forcefield.create_openmm_system(topology) # delete all forces except PeriodicTorsionForce is_torsion = ( lambda force: "PeriodicTorsionForce" in force.__class__.__name__) for i in range(openmm_system.getNumForces())[::-1]: if not is_torsion(openmm_system.getForce(i)): openmm_system.removeForce(i) assert openmm_system.getNumForces() == 1 torsion_force = openmm_system.getForce(0) assert is_torsion(torsion_force) # set k = 0 for any torsion that's not an improper indices = set( map( tuple, esp.graphs.utils.offmol_indices.improper_torsion_indices(molecule), )) num_impropers_retained = 0 for i in range(torsion_force.getNumTorsions()): ( p1, p2, p3, p4, periodicity, phase, k, ) = torsion_force.getTorsionParameters(i) if (p1, p2, p3, p4) in indices: num_impropers_retained += 1 else: torsion_force.setTorsionParameters(i, p1, p2, p3, p4, periodicity, phase, 0.0) assert (num_impropers_retained > 0 ) # otherwise this molecule is not a useful test case! return openmm_system, topology, g
def test_from_openmm_missing_reference(self): """Test creation of an openforcefield Topology object from an OpenMM Topology when missing a unique molecule""" from simtk.openmm import app pdbfile = app.PDBFile( get_data_file_path( 'systems/packmol_boxes/cyclohexane_ethanol_0.4_0.6.pdb')) molecules = [create_ethanol()] with pytest.raises( ValueError, match='No match found for molecule C6H12') as excinfo: topology = Topology.from_openmm(pdbfile.topology, unique_molecules=molecules)
def test_box_vectors(self): """Test the getter and setter for box_vectors""" topology = Topology() good_box_vectors = unit.Quantity(np.eye(3) * 20 * unit.angstrom) one_dim_vectors = unit.Quantity(np.ones(3) * 20 * unit.angstrom) bad_shape_vectors = unit.Quantity(np.ones(2) * 20 * unit.angstrom) bad_units_vectors = unit.Quantity(np.ones(3) * 20 * unit.year) unitless_vectors = np.array([10, 20, 30]) assert topology.box_vectors is None for bad_vectors in [ bad_shape_vectors, bad_units_vectors, unitless_vectors, ]: with self.assertRaises(InvalidBoxVectorsError): topology.box_vectors = bad_vectors assert topology.box_vectors is None for good_vectors in [good_box_vectors, one_dim_vectors]: topology.box_vectors = good_vectors assert (topology.box_vectors == good_vectors * np.eye(3)).all()