def test_cutoff_electrostatics(): ion_ff = ForceField(get_test_file_path("ions.offxml")) ions = Topology.from_molecules([ Molecule.from_smiles("[#3]"), Molecule.from_smiles("[#17]"), ]) out = Interchange.from_smirnoff(ion_ff, ions) out.box = [4, 4, 4] * unit.nanometer gmx = [] lmp = [] for d in np.linspace(0.75, 0.95, 5): positions = np.zeros((2, 3)) * unit.nanometer positions[1, 0] = d * unit.nanometer out.positions = positions out["Electrostatics"].method = "cutoff" gmx.append( get_gromacs_energies(out, mdp="auto").energies["Electrostatics"].m) lmp.append( get_lammps_energies(out).energies["Electrostatics"].m_as( unit.kilojoule / unit.mol)) assert np.sum(np.sqrt(np.square(np.asarray(lmp) - np.asarray(gmx)))) < 1e-3
def top_from_smiles( smiles: str, n_molecules: int = 1, ) -> Topology: """Create a gas phase OpenFF Topology from a single-molecule SMILES Parameters ---------- smiles : str The SMILES of the input molecule n_molecules : int, optional, default = 1 The number of copies of the SMILES molecule from which to compose a topology Returns ------- top : opennff.toolkit.topology.Topology A single-molecule, gas phase-like topology """ mol = Molecule.from_smiles(smiles) mol.generate_conformers(n_conformers=1) top = Topology.from_molecules(n_molecules * [mol]) # Add dummy box vectors # TODO: Revisit if/after Topology.is_periodic top.box_vectors = np.eye(3) * 10 * unit.nanometer return top
def test_constraint_reassignment(self, parsley): """Test that constraints already existing in a parametrized system can be updated against new force field data""" # TODO: Replace with more minimal force field top = Topology.from_molecules(Molecule.from_smiles("CCO")) constrained = parsley.create_openff_system(top) assert len(constrained.handlers["Constraints"].slot_map.keys()) == 6 # Update Parsley to also constrain C-O bonds parsley["Constraints"].add_parameter({"smirks": "[#6:1]-[#8:2]"}) constrained.handlers["Constraints"].store_matches( parameter_handler=parsley["Constraints"], topology=top, ) from openff.system.components.smirnoff import SMIRNOFFBondHandler bond_handler = SMIRNOFFBondHandler() bond_handler.store_matches(parameter_handler=parsley["Bonds"], topology=top) bond_handler.store_potentials(parameter_handler=parsley["Bonds"]) constrained.handlers["Constraints"].store_constraints( parameter_handler=parsley["Constraints"], bond_handler=bond_handler, ) assert len(constrained.handlers["Constraints"].slot_map.keys()) == 7
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 _off_handler(cls, molecule, **kwargs): forcefield = cls._get_forcefield(**kwargs) topology = Topology.from_molecules(molecule) openmm_system = forcefield.create_openmm_system( topology, charge_from_molecules=[molecule]) # ligand_pmd.title = cls.smiles # for i in ligand_pmd.residues: # i.name = 'LIG' #XXX no longer needed when using omm_top to create parmed structure tmp_dir = tempfile.mkdtemp() # We need all molecules as both pdb files (as packmol input) # and mdtraj.Trajectory for restoring bonds later. pdb_filename = tempfile.mktemp(suffix=".pdb", dir=tmp_dir) # XXX legacy code for save a pdb copy for simulation box creation # Chem.MolToPDBFile(mol, pdb_filename) # from openeye import oechem # OpenEye Python toolkits # oechem.OEWriteMolecule( oechem.oemolostream( pdb_filename ), mol) # XXX swtich to off save pdb # ligand_pmd.save(pdb_filename, overwrite=True) molecule.to_file(pdb_filename, "pdb") omm_top = PDBFile(pdb_filename).topology # ligand_pmd = parmed.openmm.topsystem.load_topology(topology.to_openmm(), openmm_system, molecule._conformers[0]) #XXX off topology does not keep atom names and resnames, use omm topology instead ligand_pmd = parmed.openmm.topsystem.load_topology( omm_top, openmm_system, molecule._conformers[0]) return pdb_filename, ligand_pmd
def get_assigned_torsion_param(molecule, forcefield, dihedrals): """ for a molecule and specific dihedral check the assigned torsion parameter Parameters ---------- molecule: openforcefield molecule object ff: ForceField offxml file dihedrals: list of atom indices in the dihedral Returns ------- parameter.id: str of the torsion parameter associated with the dihedral """ 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(): if force_tag == "ProperTorsions": for (atom_indices, parameter) in force_dict.items(): if atom_indices == tuple(dihedrals) or tuple( reversed(atom_indices) ) == tuple(dihedrals): return parameter.id
def test_from_openmm_single_mols(mol, n_mols): """ Test that ForceField.create_openmm_system and System.to_openmm produce objects with similar energies TODO: Tighten tolerances TODO: Test periodic and non-periodic """ parsley = ForceField(get_test_file_path("parsley.offxml")) mol = Molecule.from_smiles(mol) mol.generate_conformers(n_conformers=1) top = Topology.from_molecules(n_mols * [mol]) mol.conformers[0] -= np.min(mol.conformers) * unit.angstrom top.box_vectors = np.eye(3) * np.asarray([10, 10, 10]) * unit.nanometer if n_mols == 1: positions = mol.conformers[0] elif n_mols == 2: positions = np.vstack( [mol.conformers[0], mol.conformers[0] + 3 * unit.nanometer]) positions = positions * unit.angstrom toolkit_system = parsley.create_openmm_system(top) native_system = parsley.create_openff_system(topology=top).to_openmm() compare_system_energies( system1=toolkit_system, system2=native_system, positions=positions, box_vectors=top.box_vectors, )
def test_gro_file_all_zero_positions(self, parsley): top = Topology.from_molecules(Molecule.from_smiles("CC")) zero_positions = Interchange.from_smirnoff(force_field=parsley, topology=top) zero_positions.positions = np.zeros( (top.n_topology_atoms, 3)) * unit.nanometer with pytest.warns(UserWarning, match="seem to all be zero"): zero_positions.to_gro("foo.gro")
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_force_field_no_constraints(self, parsley_unconstrained): """Test that a force field _without_ a Constraints tag does not add a Constraints handler""" # TODO: Replace with more minimal force field top = Topology.from_molecules(Molecule.from_smiles("CC")) sys_out = parsley_unconstrained.create_openff_system(top) assert "Constraints" not in sys_out.handlers.keys()
def test_parmed_openmm(tmpdir, smiles): tmpdir.chdir() parsley = ForceField("openff_unconstrained-1.0.0.offxml") mol = Molecule.from_smiles(smiles) mol.generate_conformers(n_conformers=1) top = Topology.from_molecules(mol) box = 4 * np.eye(3) * unit.nanometer with tempfile.TemporaryDirectory() as omm_tempdir: with temporary_cd(omm_tempdir): openff_openmm_pmd_gmx( topology=top, forcefield=parsley, box=box, prefix="via_openmm", ) ener1, ener1_file = gmx_energy( top="via_openmm.top", gro="via_openmm.gro", mdp=resource_filename("intermol", "tests/gromacs/grompp.mdp"), ) with tempfile.TemporaryDirectory() as off_tempdir: with temporary_cd(off_tempdir): openff_pmd_gmx_indirect( topology=top, forcefield=parsley, box=box, prefix="via_conversion", ) ener2, ener2_file = gmx_energy( top="via_conversion.top", gro="via_conversion.gro", mdp=resource_filename("intermol", "tests/gromacs/grompp.mdp"), ) compare_energies(ener1, ener2) with tempfile.TemporaryDirectory() as off_tempdir: with temporary_cd(off_tempdir): openff_pmd_gmx_direct( topology=top, forcefield=parsley, box=box, prefix="via_call", ) ener3, ener3_file = gmx_energy( top="via_call.top", gro="via_call.gro", mdp=resource_filename("intermol", "tests/gromacs/grompp.mdp"), ) compare_energies(ener2, ener3)
def test_nth_degree_neighbors(n_degrees, num_pairs): pass smiles = ["c1ccccc1", "N1ONON1"] topology = Topology.from_molecules( [Molecule.from_smiles(smi) for smi in smiles]) # See test_molecule.TestMolecule.test_nth_degree_neighbors_rings for values num_pairs_found = len( [*topology.nth_degree_neighbors(n_degrees=n_degrees)]) assert num_pairs_found == num_pairs
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_library_charge_assignment(self): from openff.toolkit.tests.test_forcefield import xml_ethanol_library_charges_ff from openff.system.stubs import ForceField forcefield = ForceField("openff-1.3.0.offxml") top = Topology.from_molecules( [Molecule.from_smiles(smi) for smi in ["[Na+]", "[Cl-]"]]) forcefield.create_openff_system(top)
def test_default_am1bcc_charge_assignment(self, parsley): top = Topology.from_molecules([ Molecule.from_smiles("C"), Molecule.from_smiles("C=C"), Molecule.from_smiles("CCO"), ]) reference = parsley.create_openmm_system(top) new = parsley.create_openff_system(top) compare_charges_omm_off(reference, new)
def test_from_parsley(self, parsley): top = Topology.from_molecules( [Molecule.from_smiles("CCO"), Molecule.from_smiles("CC")]) out = parsley.create_openff_system(top) assert "Constraints" in out.handlers.keys() assert "Bonds" in out.handlers.keys() assert "Angles" in out.handlers.keys() assert "ProperTorsions" in out.handlers.keys() assert "vdW" in out.handlers.keys()
def test_library_charge_assignment(self): from openff.system.stubs import ForceField forcefield = ForceField("openff-1.3.0.offxml") forcefield.deregister_parameter_handler("ToolkitAM1BCC") top = Topology.from_molecules( [Molecule.from_smiles(smi) for smi in ["[Na+]", "[Cl-]"]]) reference = forcefield.create_openmm_system(top) new = forcefield.create_openff_system(top) compare_charges_omm_off(reference, new)
def test_no_matched_constraints(self, parsley): """Test that a force field with a Constraints tag adds an empty Constraints handler when the topology does not match any parameters""" # TODO: Replace with more minimal force field top = Topology.from_molecules(Molecule.from_smiles("O=C=O")) sys_out = parsley.create_openff_system(top) assert "Constraints" in sys_out.handlers.keys() constraints = sys_out.handlers["Constraints"] assert constraints.slot_map == dict() assert constraints.constraints == dict()
def test_to_lammps_single_mols(mol, n_mols): """ Test that ForceField.create_openmm_system and System.to_openmm produce objects with similar energies TODO: Tighten tolerances TODO: Test periodic and non-periodic """ parsley = ForceField("openff_unconstrained-1.0.0.offxml") mol = Molecule.from_smiles(mol) mol.generate_conformers(n_conformers=1) top = Topology.from_molecules(n_mols * [mol]) mol.conformers[0] -= np.min(mol.conformers) * omm_unit.angstrom top.box_vectors = np.eye(3) * np.asarray([10, 10, 10]) * omm_unit.nanometer if n_mols == 1: positions = mol.conformers[0] elif n_mols == 2: positions = np.vstack( [mol.conformers[0], mol.conformers[0] + 3 * omm_unit.nanometer] ) positions = positions * omm_unit.angstrom openff_sys = parsley.create_openff_system(topology=top) openff_sys.positions = positions.value_in_unit(omm_unit.nanometer) openff_sys.box = top.box_vectors reference = get_openmm_energies( off_sys=openff_sys, round_positions=3, electrostatics=False ) lmp_energies = get_lammps_energies( off_sys=openff_sys, round_positions=3, electrostatics=False ) _write_lammps_input( off_sys=openff_sys, file_name="tmp.in", electrostatics=False, ) lmp_energies.compare( reference, custom_tolerances={ "Nonbonded": 999 * omm_unit.kilojoule_per_mole, "Torsion": 0.005 * omm_unit.kilojoule_per_mole, }, )
def test_multi_mol_topology_to_compound(self): """Test the basic behavior of a (multi-mol) OFFTop""" ethanol = Molecule.from_smiles("CCO") ethanol.name = "ETH" methane = Molecule.from_smiles("C") methane.name = "MET" top = Topology.from_molecules([ethanol, ethanol, methane, methane]) comp = offtop_to_compound(top) assert comp.n_particles == 28 # 2 * (9 + 5) assert comp.n_bonds == 24 # 2 * (8 + 4) assert len(comp.children) == 4 # 4 "molecules"
def water_box_topology() -> Topology: mol = Molecule.from_smiles("O") mol.generate_conformers() n_molecules = 256 topology: Topology = Topology.from_molecules([mol] * n_molecules) # Create some coordinates (without the v-sites) and estimate box vectors. topology.box_vectors = (numpy.eye(3) * math.ceil(n_molecules**(1 / 3) + 2) * 2.5 * unit.angstrom) return topology
def checkTorsion(molList, 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 = [] for mol in molList: molecule = Molecule.from_mapped_smiles(mol.GetData("cmiles")) 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 = [] # 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) if atom_indices == mol.GetData("TDindices") or tuple( reversed(atom_indices) ) == mol.GetData("TDindices"): count += 1 mol.SetData("IDMatch", parameter.id) mols.append(mol) print( "Out of " + str(len(molList)) + " molecules, " + str(count) + " were processed with checkTorsion()" ) return mols
def test_force_field_basic_constraints(self, parsley): """Test that a force field Constraints tag adds a Constraints handler with expected data""" # TODO: Replace with more minimal force field top = Topology.from_molecules(Molecule.from_smiles("CC")) sys_out = parsley.create_openff_system(top) assert "Constraints" in sys_out.handlers.keys() constraints = sys_out.handlers["Constraints"] assert "(0, 1)" not in constraints.slot_map.keys() # C-C bond assert "(0, 2)" in constraints.slot_map.keys() # C-H bond assert len(constraints.slot_map.keys()) == 6 # number of C-H bonds assert len({constraints.slot_map.values()}) == 1 # always True assert constraints.constraints[constraints.slot_map["(0, 2)"]] is True
def test_to_openmm_assign_unique_atom_names(self): """ Ensure that OFF topologies with no pre-existing atom names have unique atom names applied when being converted to openmm """ # 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]) omm_topology = off_topology.to_openmm() atom_names = set() for atom in omm_topology.atoms(): atom_names.add(atom.name) # There should be 6 unique Cs, 6 unique Hs, and 1 unique O, for a total of 13 unique atom names assert len(atom_names) == 13
def test_argon_buck(): mol = Molecule.from_smiles("[#18]") top = Topology.from_molecules([mol, mol]) A = 1.69e-8 * unit.Unit("erg / mol") * Avogadro B = 1 / (0.273 * unit.angstrom) C = 102e-12 * unit.Unit("erg / mol") * unit.angstrom**6 * Avogadro A = A.to(unit.Unit("kilojoule/mol")) B = B.to(unit.Unit("1 / nanometer")) C = C.to(unit.Unit("kilojoule / mol * nanometer ** 6")) r = 0.3 * unit.nanometer buck = BuckinghamvdWHandler() coul = ElectrostaticsMetaHandler() # Just to pass compatibility checks pot_key = PotentialKey(id="[#18]") pot = Potential(parameters={"A": A, "B": B, "C": C}) for top_atom in top.topology_atoms: top_key = TopologyKey(atom_indices=(top_atom.topology_atom_index, )) buck.slot_map.update({top_key: pot_key}) coul.charges.update({top_key: 0 * unit.elementary_charge}) buck.potentials[pot_key] = pot from openff.system.components.system import System out = System() out.handlers["Buckingham-6"] = buck out.handlers["Electrostatics"] = coul out.topology = top out.box = [10, 10, 10] * unit.nanometer out.positions = [[0, 0, 0], [0.3, 0, 0]] * unit.nanometer out.to_gro("out.gro", writer="internal") out.to_top("out.top", writer="internal") gmx_energies = get_gromacs_energies(out, mdp="cutoff_buck") omm_energies = get_openmm_energies(out) by_hand = A * exp(-B * r) - C * r**-6 gmx_energies.compare(omm_energies) resid = simtk_to_pint(gmx_energies.energies["Nonbonded"]) - by_hand assert resid < 1e-5 * unit.kilojoule / unit.mol
def test_pruned_impropers(self): """Test {smirnoff|amber}_impropers from the Topology API""" top = Topology.from_molecules( [Molecule.from_smiles(smi) for smi in ["N", "C=C"]]) assert len([*top.smirnoff_impropers]) == 18 assert len([*top.amber_impropers]) == 18 # Order not guaranteed, so cannot zip and compare directly for smirnoff_imp in top.smirnoff_impropers: # Convert SMIRNOFF-style improper into AMBER-style mod_imp = ( smirnoff_imp[1], smirnoff_imp[0], smirnoff_imp[2], smirnoff_imp[3], ) assert mod_imp in top.amber_impropers
def test_smirnoff_hack(): """Test basic behavior of smirnoff_hack.py, in particular the compatibility with Molecule API""" from openff.toolkit.topology import Molecule, Topology from openff.toolkit.typing.engines.smirnoff import ForceField from openff.toolkit.utils.toolkits import (AmberToolsToolkitWrapper, RDKitToolkitWrapper, ToolkitRegistry) from forcebalance import smirnoff_hack top = Topology.from_molecules(Molecule.from_smiles("CCO")) parsley = ForceField("openff-1.0.0.offxml") registry = ToolkitRegistry() registry.register_toolkit(RDKitToolkitWrapper) registry.register_toolkit(AmberToolsToolkitWrapper) parsley.create_openmm_system(top, toolkit_registry=registry)
def test_from_openmm_single_mols(mol, n_mols): """ Test that ForceField.create_openmm_system and Interchange.to_openmm produce objects with similar energies TODO: Tighten tolerances TODO: Test periodic and non-periodic """ parsley = ForceField(get_test_file_path("parsley.offxml")) mol = Molecule.from_smiles(mol) mol.generate_conformers(n_conformers=1) top = Topology.from_molecules(n_mols * [mol]) mol.conformers[0] -= np.min(mol.conformers) * simtk_unit.angstrom top.box_vectors = np.eye(3) * np.asarray([15, 15, 15]) * simtk_unit.nanometer if n_mols == 1: positions = mol.conformers[0] elif n_mols == 2: positions = np.vstack( [mol.conformers[0], mol.conformers[0] + 3 * simtk_unit.nanometer] ) positions = positions * simtk_unit.angstrom toolkit_system = parsley.create_openmm_system(top) native_system = Interchange.from_smirnoff( force_field=parsley, topology=top ).to_openmm() toolkit_energy = _get_openmm_energies( omm_sys=toolkit_system, box_vectors=toolkit_system.getDefaultPeriodicBoxVectors(), positions=positions, ) native_energy = _get_openmm_energies( omm_sys=native_system, box_vectors=native_system.getDefaultPeriodicBoxVectors(), positions=positions, ) toolkit_energy.compare(native_energy)
def test_argon(n_mol): from openff.system.utils import get_test_file_path ar_ff = ForceField(get_test_file_path("argon.offxml")) mol = Molecule.from_smiles("[#18]") mol.add_conformer(np.array([[0, 0, 0]]) * omm_unit.angstrom) mol.name = "FOO" top = Topology.from_molecules(n_mol * [mol]) off_sys = ar_ff.create_openff_system(top) mol.to_file("out.xyz", file_format="xyz") compound: mb.Compound = mb.load("out.xyz") packed_box: mb.Compound = mb.fill_box( compound=compound, n_compounds=[n_mol], box=mb.Box([4, 4, 4]), ) positions = packed_box.xyz * unit.nanometer positions = np.round(positions, 3) off_sys.positions = positions box = np.asarray(packed_box.box.lengths) * unit.nanometer off_sys.box = box omm_energies = get_openmm_energies( off_sys, round_positions=8, hard_cutoff=True, electrostatics=False ) gmx_energies = get_gromacs_energies( off_sys, writer="internal", electrostatics=False ) lmp_energies = get_lammps_energies(off_sys) omm_energies.compare(lmp_energies) omm_energies.compare( gmx_energies, custom_tolerances={ "Nonbonded": 2e-5 * omm_unit.kilojoule_per_mole, }, )
def test_to_openmm_assign_some_unique_atom_names(self): """ Ensure that OFF topologies with some pre-existing atom names have unique atom names applied to the other atoms when being converted to openmm """ # Create OpenFF topology with 1 ethanol and 2 benzenes. ethanol = Molecule.from_smiles("CCO") for atom in ethanol.atoms: atom.name = f"AT{atom.molecule_atom_index}" benzene = Molecule.from_smiles("c1ccccc1") 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 9 "ATOM#"-labeled atoms, 6 unique Cs, and 6 unique Hs, # for a total of 21 unique atom names assert len(atom_names) == 21