diff --git a/devtools/conda-envs/dev_env.yaml b/devtools/conda-envs/dev_env.yaml index 77547ef41..4eec7d5a6 100644 --- a/devtools/conda-envs/dev_env.yaml +++ b/devtools/conda-envs/dev_env.yaml @@ -27,6 +27,7 @@ dependencies: - pytest - pytest-cov - pytest-xdist + - pytest-randomly - nbval - qcportal - openff-qcsubmit diff --git a/openff/interchange/_tests/__init__.py b/openff/interchange/_tests/__init__.py index 10f386335..69a04d010 100644 --- a/openff/interchange/_tests/__init__.py +++ b/openff/interchange/_tests/__init__.py @@ -7,10 +7,9 @@ import numpy as np import openmm import pytest -from openff.toolkit._tests.create_molecules import create_ammonia, create_ethanol +from openff.toolkit import ForceField, Molecule, Topology +from openff.toolkit._tests.create_molecules import create_ethanol from openff.toolkit._tests.utils import get_data_file_path -from openff.toolkit.topology import Molecule, Topology -from openff.toolkit.typing.engines.smirnoff import ForceField from openff.units import unit from openff.utilities.utilities import has_executable from openmm import unit as openmm_unit @@ -55,36 +54,23 @@ def get_test_files_dir_path(dirname: Optional[str] = None) -> pathlib.Path: ) -class _BaseTest: - @pytest.fixture(autouse=True) - def _initdir(self, tmpdir): - tmpdir.chdir() +class MoleculeWithConformer(Molecule): + """Thin wrapper around `Molecule` to produce an instance with a conformer in one call.""" - # TODO: group fixtures up as dicts, i.e. argon['forcefield'], argon['topology'], ... - @pytest.fixture() - def argon_ff(self): - """Fixture that loads an SMIRNOFF XML for argon.""" - return ForceField(get_test_file_path("argon.offxml")) + @classmethod + def from_smiles(self, smiles, name=""): + """Create from smiles and generate a single conformer.""" + molecule = super().from_smiles(smiles) + molecule.generate_conformers(n_conformers=1) + molecule.name = name - @pytest.fixture() - def argon(self): - """Fixture that builds a simple arogon topology.""" - argon = Molecule() - argon.add_atom( - atomic_number=18, - formal_charge=0, - is_aromatic=False, - ) - return argon + return molecule - @pytest.fixture() - def ammonia_ff(self): - """Fixture that loads an SMIRNOFF XML for ammonia.""" - return ForceField(get_test_file_path("ammonia.offxml")) - @pytest.fixture() - def ammonia(self): - return create_ammonia() +class _BaseTest: + @pytest.fixture(autouse=True) + def _initdir(self, tmpdir): + tmpdir.chdir() @pytest.fixture() def ethanol(self): @@ -96,11 +82,6 @@ def basic_top(self): top.box_vectors = unit.Quantity([5, 5, 5], unit.nanometer) return top - @pytest.fixture() - def ammonia_top(self, ammonia): - """Fixture that builds a simple ammonia topology.""" - return Topology.from_molecules(4 * [ammonia]) - @pytest.fixture() def ethanol_top(self, ethanol): """Fixture that builds a simple four ethanol topology.""" @@ -136,105 +117,6 @@ def mainchain_arg(self): def two_peptides(self, mainchain_ala, mainchain_arg): return Topology.from_molecules([mainchain_ala, mainchain_arg]) - @pytest.fixture() - def tip3p_xml(self): - # Modified (added Electrostatics tag) from below link - # https://github.com/openforcefield/openff-toolkit/blob/0.10.2/openff/toolkit/data/test_forcefields/tip3p.offxml - return """ - - - - - - - - - - - - - - -""" - - @pytest.fixture() - def tip3p(self, tip3p_xml): - return ForceField(tip3p_xml) - - @pytest.fixture() - def tip3p_missing_electrostatics_xml(self): - # Stripped from below, follow link for details - # https://github.com/openforcefield/openff-toolkit/blob/0.10.2/openff/toolkit/data/test_forcefields/tip3p.offxml - return """ - - - - - - - - -""" - xml_ff_bo_bonds = """ @@ -284,10 +166,6 @@ def acetaldehyde(self): "[C:1]([C:2](=[O:3])[H:7])([H:4])([H:5])[H:6]", ) - @pytest.fixture() - def water(self): - return Molecule.from_mapped_smiles("[H:2][O:1][H:3]") - HAS_GROMACS = _find_gromacs_executable() is not None HAS_LAMMPS = _find_lammps_executable() is not None diff --git a/openff/interchange/_tests/conftest.py b/openff/interchange/_tests/conftest.py new file mode 100644 index 000000000..57ee11bb6 --- /dev/null +++ b/openff/interchange/_tests/conftest.py @@ -0,0 +1,36 @@ +"""Pytest configuration.""" +import pytest +from openff.toolkit import ForceField, Molecule + +from openff.interchange._tests import get_test_file_path + + +@pytest.fixture() +def _simple_force_field(): + # TODO: Create a minimal force field for faster tests + pass + + +@pytest.fixture() +def tip3p() -> ForceField: + return ForceField("tip3p.offxml") + + +@pytest.fixture() +def tip4p() -> ForceField: + return ForceField("tip4p_fb.offxml") + + +@pytest.fixture() +def gbsa_force_field() -> ForceField: + return ForceField( + "openff-2.0.0.offxml", + get_test_file_path("gbsa.offxml"), + ) + + +@pytest.fixture(scope="session") +def water() -> Molecule: + molecule = Molecule.from_mapped_smiles("[H:2][O:1][H:3]") + molecule.generate_conformers(n_conformers=1) + return molecule diff --git a/openff/interchange/_tests/data/argon.offxml b/openff/interchange/_tests/data/argon.offxml deleted file mode 100644 index 4fb264e35..000000000 --- a/openff/interchange/_tests/data/argon.offxml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/openff/interchange/_tests/energy_tests/components/tests_smirnoff.py b/openff/interchange/_tests/energy_tests/components/tests_smirnoff.py index 51d8e386e..9e439bcc9 100644 --- a/openff/interchange/_tests/energy_tests/components/tests_smirnoff.py +++ b/openff/interchange/_tests/energy_tests/components/tests_smirnoff.py @@ -1,9 +1,8 @@ import numpy as np import openmm import pytest +from openff.toolkit import ForceField, Molecule from openff.toolkit._tests.utils import get_data_file_path -from openff.toolkit.topology import Molecule -from openff.toolkit.typing.engines.smirnoff.forcefield import ForceField from openff.units import unit from openmm import unit as openmm_unit diff --git a/openff/interchange/_tests/energy_tests/test_energies.py b/openff/interchange/_tests/energy_tests/test_energies.py index 32bf99b07..302b215c0 100644 --- a/openff/interchange/_tests/energy_tests/test_energies.py +++ b/openff/interchange/_tests/energy_tests/test_energies.py @@ -11,6 +11,7 @@ from openff.interchange._tests import ( HAS_GROMACS, HAS_LAMMPS, + MoleculeWithConformer, _BaseTest, get_test_file_path, needs_gmx, @@ -45,17 +46,15 @@ def oplsaa(self): def test_energies_single_mol(self, constrained, sage, sage_unconstrained, mol_smi): import mbuild as mb - mol = Molecule.from_smiles(mol_smi) - mol.generate_conformers(n_conformers=1) - mol.name = "FOO" + molecule = MoleculeWithConformer.from_smiles(mol_smi, name="FOO") force_field = sage if constrained else sage_unconstrained - interchange = Interchange.from_smirnoff(force_field, [mol]) + interchange = Interchange.from_smirnoff(force_field, [molecule]) interchange.collections["Electrostatics"].periodic_potential = "cutoff" - mol.to_file("out.xyz", file_format="xyz") + molecule.to_file("out.xyz", file_format="xyz") compound = mb.load("out.xyz") packed_box = mb.fill_box( compound=compound, @@ -107,8 +106,7 @@ def test_process_rb_torsions(self, oplsaa): from openff.interchange.components.mbuild import offmol_to_compound - ethanol = Molecule.from_smiles("CCO") - ethanol.generate_conformers(n_conformers=1) + ethanol = MoleculeWithConformer.from_smiles("CCO") ethanol.generate_unique_atom_names() my_compound = offmol_to_compound(ethanol) @@ -136,9 +134,7 @@ def test_gmx_14_energies_exist(self, sage): # Use a molecule with only one 1-4 interaction, and # make it between heavy atoms because H-H 1-4 are weak - mol = Molecule.from_smiles("ClC#CCl") - mol.name = "HPER" - mol.generate_conformers(n_conformers=1) + mol = MoleculeWithConformer.from_smiles("ClC#CCl", name="HPER") out = Interchange.from_smirnoff(sage, [mol]) out.positions = mol.conformers[0] @@ -215,17 +211,16 @@ def test_interpolated_parameters(self, smi): """ - mol = Molecule.from_smiles(smi) - mol.generate_conformers(n_conformers=1) + molecule = MoleculeWithConformer.from_smiles(smi) forcefield = ForceField( "openff-2.0.0.offxml", xml_ff_bo_all_heavy_bonds, ) - out = Interchange.from_smirnoff(forcefield, [mol]) + out = Interchange.from_smirnoff(forcefield, [molecule]) out.box = [4, 4, 4] * unit.nanometer - out.positions = mol.conformers[0] + out.positions = molecule.conformers[0] for key in ["Bond", "Torsion"]: interchange_energy = get_openmm_energies( diff --git a/openff/interchange/_tests/interoperability_tests/internal/test_gromacs.py b/openff/interchange/_tests/interoperability_tests/internal/test_gromacs.py index d461187a3..fd66d641d 100644 --- a/openff/interchange/_tests/interoperability_tests/internal/test_gromacs.py +++ b/openff/interchange/_tests/interoperability_tests/internal/test_gromacs.py @@ -15,7 +15,7 @@ from openmm import unit as openmm_unit from openff.interchange import Interchange -from openff.interchange._tests import _BaseTest, needs_gmx +from openff.interchange._tests import MoleculeWithConformer, _BaseTest, needs_gmx from openff.interchange.components.nonbonded import BuckinghamvdWCollection from openff.interchange.components.potentials import Potential from openff.interchange.drivers import get_gromacs_energies, get_openmm_energies @@ -93,8 +93,7 @@ def converter(x): assert n_decimals == 12 def test_vaccum_warning(self, sage): - molecule = Molecule.from_smiles("CCO") - molecule.generate_conformers(n_conformers=1) + molecule = MoleculeWithConformer.from_smiles("CCO") out = Interchange.from_smirnoff(force_field=sage, topology=[molecule]) @@ -167,9 +166,8 @@ class TestGROMACS(_BaseTest): ], ) def test_simple_roundtrip(self, sage, smiles, reader): - molecule = Molecule.from_smiles(smiles) + molecule = MoleculeWithConformer.from_smiles(smiles) molecule.name = molecule.to_hill_formula() - molecule.generate_conformers(n_conformers=1) topology = molecule.to_topology() out = Interchange.from_smirnoff(force_field=sage, topology=topology) @@ -194,8 +192,11 @@ def test_simple_roundtrip(self, sage, smiles, reader): @skip_if_missing("parmed") def test_num_impropers(self, sage): - top = Molecule.from_smiles("CC1=CC=CC=C1").to_topology() - out = Interchange.from_smirnoff(sage, top) + out = Interchange.from_smirnoff( + sage, + MoleculeWithConformer.from_smiles("CC1=CC=CC=C1").to_topology(), + ) + out.box = unit.Quantity(4 * numpy.eye(3), units=unit.nanometer) out.to_top("tmp.top") @@ -279,8 +280,8 @@ def test_argon_buck(self): SMIRNOFFElectrostaticsCollection, ) - mol = Molecule.from_smiles("[#18]") - mol.name = "Argon" + mol = MoleculeWithConformer.from_smiles("[#18]", name="Argon") + top = Topology.from_molecules([mol, mol]) # http://www.sklogwiki.org/SklogWiki/index.php/Argon#Buckingham_potential @@ -421,16 +422,14 @@ def sage_with_monovalent_lone_pair(self, sage): @skip_if_missing("parmed") def test_sigma_hole_example(self, sage_with_sigma_hole): """Test that a single-molecule sigma hole example runs""" - mol = Molecule.from_smiles("CCl") - mol.name = "Chloromethane" - mol.generate_conformers(n_conformers=1) + molecule = MoleculeWithConformer.from_smiles("CCl", name="Chloromethane") out = Interchange.from_smirnoff( force_field=sage_with_sigma_hole, - topology=mol.to_topology(), + topology=molecule.to_topology(), ) out.box = [4, 4, 4] - out.positions = mol.conformers[0] + out.positions = molecule.conformers[0] # TODO: Sanity-check reported energies get_gromacs_energies(out) @@ -442,9 +441,7 @@ def test_sigma_hole_example(self, sage_with_sigma_hole): def test_carbonyl_example(self, sage_with_monovalent_lone_pair): """Test that a single-molecule DivalentLonePair example runs""" - mol = Molecule.from_smiles("C=O") - mol.name = "Carbon_monoxide" - mol.generate_conformers(n_conformers=1) + mol = MoleculeWithConformer.from_smiles("C=O", name="Carbon_monoxide") out = Interchange.from_smirnoff( force_field=sage_with_monovalent_lone_pair, diff --git a/openff/interchange/_tests/interoperability_tests/test_external.py b/openff/interchange/_tests/interoperability_tests/test_external.py deleted file mode 100644 index 1a3bffd63..000000000 --- a/openff/interchange/_tests/interoperability_tests/test_external.py +++ /dev/null @@ -1,29 +0,0 @@ -import numpy as np -import pytest -from openff.toolkit.topology import Molecule, Topology -from openff.units import unit -from openff.units.openmm import from_openmm -from openmm import app -from openmm import unit as openmm_unit - -from openff.interchange import Interchange -from openff.interchange._tests import _BaseTest, get_test_file_path - - -class TestFromOpenMM(_BaseTest): - @pytest.mark.slow() - def test_from_openmm_pdbfile(self, argon_ff): - pdb_file_path = get_test_file_path("10-argons.pdb").as_posix() - pdbfile = app.PDBFile(pdb_file_path) - - mol = Molecule.from_smiles("[#18]") - tmp = Topology.from_openmm(pdbfile.topology, unique_molecules=[mol]) - - out = Interchange.from_smirnoff(argon_ff, tmp) - out.box = from_openmm(pdbfile.topology.getPeriodicBoxVectors()) - out.positions = from_openmm(pdbfile.getPositions()) - - assert np.allclose( - out.positions.to(unit.nanometer).magnitude, - pdbfile.getPositions().value_in_unit(openmm_unit.nanometer), - ) diff --git a/openff/interchange/_tests/interoperability_tests/test_openmm.py b/openff/interchange/_tests/interoperability_tests/test_openmm.py index ee713cc7d..3fc91f085 100644 --- a/openff/interchange/_tests/interoperability_tests/test_openmm.py +++ b/openff/interchange/_tests/interoperability_tests/test_openmm.py @@ -12,7 +12,11 @@ from openff.units import unit from openff.interchange import Interchange -from openff.interchange._tests import _BaseTest, get_test_file_path +from openff.interchange._tests import ( + MoleculeWithConformer, + _BaseTest, + get_test_file_path, +) from openff.interchange._tests.unit_tests.plugins.test_smirnoff_plugins import ( TestDoubleExponential, ) @@ -229,14 +233,11 @@ def test_geometric_mixing_rule(self): @pytest.mark.slow() @pytest.mark.parametrize("mol_smi", ["C", "CC", "CCO"]) def test_openmm_roundtrip(self, sage, mol_smi): - mol = Molecule.from_smiles(mol_smi) - mol.generate_conformers(n_conformers=1) - top = mol.to_topology() + topology = MoleculeWithConformer.from_smiles(mol_smi).to_topology() - interchange = Interchange.from_smirnoff(sage, top) + interchange = Interchange.from_smirnoff(sage, topology) interchange.box = [4, 4, 4] - interchange.positions = mol.conformers[0].value_in_unit(openmm.unit.nanometer) converted = from_openmm( topology=interchange.to_openmm_topology(), @@ -253,13 +254,13 @@ def test_openmm_roundtrip(self, sage, mol_smi): @pytest.mark.xfail(reason="Broken because of splitting non-bonded forces") @pytest.mark.slow() def test_combine_nonbonded_forces(self, sage): - mol = Molecule.from_smiles("ClC#CCl") - mol.name = "HPER" - mol.generate_conformers(n_conformers=1) + topology = MoleculeWithConformer.from_smiles( + "ClC#CCl", + name="HPER", + ).to_topology() - out = Interchange.from_smirnoff(force_field=sage, topology=mol.to_topology()) + out = Interchange.from_smirnoff(force_field=sage, topology=topology) out.box = [4, 4, 4] - out.positions = mol.conformers[0] num_forces_combined = out.to_openmm( combine_nonbonded_forces=True, @@ -278,14 +279,12 @@ def test_combine_nonbonded_forces(self, sage): separate["vdW"] + separate["Electrostatics"] - combined["Nonbonded"] ).m < 0.001 - def test_openmm_no_angle_force_if_constrained(self): - # Sage includes angle parameters for water and also TIP3P constraints - tip3p = ForceField("openff-2.0.0.offxml") - - topology = Molecule.from_smiles("O").to_topology() + def test_openmm_no_angle_force_if_constrained(self, water, sage): + topology = water.to_topology() topology.box_vectors = [4, 4, 4] * unit.nanometer - interchange = Interchange.from_smirnoff(tip3p, topology) + # Sage includes angle parameters for water and also TIP3P constraints + interchange = Interchange.from_smirnoff(sage, topology) openmm_system = interchange.to_openmm(combine_nonbonded_forces=True) # The only angle in the system (H-O-H) includes bonds with constrained lengths @@ -446,9 +445,7 @@ def test_combine_compatibility(self, de_force_field): def test_double_exponential_create_simulation(self, de_force_field): from openff.toolkit.utils.openeye_wrapper import OpenEyeToolkitWrapper - molecule = Molecule.from_smiles("CCO") - molecule.generate_conformers(n_conformers=1) - topology = molecule.to_topology() + topology = MoleculeWithConformer.from_smiles("CCO").to_topology() topology.box_vectors = unit.Quantity([4, 4, 4], unit.nanometer) out = Interchange.from_smirnoff( @@ -508,7 +505,6 @@ def sage_with_sigma_hole(self, sage): @pytest.fixture() def sage_with_monovalent_lone_pair(self, sage): - """Fixture that loads an SMIRNOFF XML for argon""" virtual_site_handler = VirtualSiteHandler(version=0.3) carbonyl_type = VirtualSiteHandler.VirtualSiteMonovalentLonePairType( @@ -529,7 +525,7 @@ def sage_with_monovalent_lone_pair(self, sage): return sage - def test_valence_term_paticle_index_offsets(self): + def test_valence_term_paticle_index_offsets(self, water): # Use a questionable version of TIP5P that includes angle parameters, since that's what's being tested tip5p_offxml = """ @@ -604,7 +600,6 @@ def test_valence_term_paticle_index_offsets(self): """ tip5p = ForceField(tip5p_offxml) - water = Molecule.from_mapped_smiles("[H:2][O:1][H:3]") out = Interchange.from_smirnoff(tip5p, [water, water]).to_openmm( combine_nonbonded_forces=True, @@ -622,10 +617,8 @@ def test_valence_term_paticle_index_offsets(self): class TestOpenMMVirtualSiteExclusions(_BaseTest): - def test_tip5p_num_exceptions(self): + def test_tip5p_num_exceptions(self, water): tip5p = ForceField(get_test_file_path("tip5p.offxml")) - water = Molecule.from_smiles("O") - water.generate_conformers(n_conformers=1) out = Interchange.from_smirnoff(tip5p, [water]).to_openmm( combine_nonbonded_forces=True, @@ -695,10 +688,7 @@ def test_dichloroethane_exceptions(self, sage): class TestToOpenMMTopology(_BaseTest): - def test_num_virtual_sites(self): - tip4p = ForceField("openff-2.0.0.offxml", get_test_file_path("tip4p.offxml")) - water = Molecule.from_smiles("O") - + def test_num_virtual_sites(self, water, tip4p): out = Interchange.from_smirnoff(tip4p, [water]) assert _get_num_virtual_sites(to_openmm_topology(out)) == 1 @@ -707,13 +697,12 @@ def test_num_virtual_sites(self): # to be used while virtual sites are present in a handler assert _get_num_virtual_sites(out.topology.to_openmm()) == 0 - def test_interchange_method(self): + def test_interchange_method(self, water, tip4p): """ Ensure similar-ish behavior between `to_openmm_topology` as a standalone function and as the wrapped method of the same name on the `Interchange` class. """ - tip4p = ForceField("openff-2.0.0.offxml", get_test_file_path("tip4p.offxml")) - topology = Molecule.from_smiles("O").to_topology() + topology = water.to_topology() topology.box_vectors = unit.Quantity([4, 4, 4], unit.nanometer) out = Interchange.from_smirnoff(tip4p, topology) @@ -721,7 +710,7 @@ def test_interchange_method(self): _compare_openmm_topologies(out.to_openmm_topology(), to_openmm_topology(out)) @pytest.mark.parametrize("ensure_unique_atom_names", [True, "residues", "chains"]) - def test_assign_unique_atom_names(self, ensure_unique_atom_names): + def test_assign_unique_atom_names(self, ensure_unique_atom_names, sage): """ Ensure that OFF topologies with no pre-existing atom names have unique atom names applied when being converted to openmm @@ -737,7 +726,6 @@ def test_assign_unique_atom_names(self, ensure_unique_atom_names): [mol._hierarchy_schemes for mol in off_topology.molecules], ), "Test assumes no hierarchy schemes" - sage = ForceField("openff-2.0.0.offxml") interchange = Interchange.from_smirnoff(sage, off_topology) omm_topology = interchange.to_openmm_topology( @@ -750,7 +738,7 @@ def test_assign_unique_atom_names(self, ensure_unique_atom_names): assert len(atom_names) == 13 @pytest.mark.parametrize("ensure_unique_atom_names", [True, "residues", "chains"]) - def test_assign_some_unique_atom_names(self, ensure_unique_atom_names): + def test_assign_some_unique_atom_names(self, ensure_unique_atom_names, sage): """ Ensure that OFF topologies with some pre-existing atom names have unique atom names applied to the other atoms when being converted to openmm @@ -768,7 +756,6 @@ def test_assign_some_unique_atom_names(self, ensure_unique_atom_names): [mol._hierarchy_schemes for mol in off_topology.molecules], ), "Test assumes no hierarchy schemes" - sage = ForceField("openff-2.0.0.offxml") interchange = Interchange.from_smirnoff(sage, off_topology) omm_topology = interchange.to_openmm_topology( @@ -782,7 +769,11 @@ def test_assign_some_unique_atom_names(self, ensure_unique_atom_names): assert len(atom_names) == 21 @pytest.mark.parametrize("ensure_unique_atom_names", [True, "residues", "chains"]) - def test_assign_unique_atom_names_some_duplicates(self, ensure_unique_atom_names): + def test_assign_unique_atom_names_some_duplicates( + self, + ensure_unique_atom_names, + sage, + ): """ Ensure that OFF topologies where some molecules have invalid/duplicate atom names have unique atom names applied while the other molecules are unaffected. @@ -810,7 +801,6 @@ def test_assign_unique_atom_names_some_duplicates(self, ensure_unique_atom_names [mol._hierarchy_schemes for mol in off_topology.molecules], ), "Test assumes no hierarchy schemes" - sage = ForceField("openff-2.0.0.offxml") interchange = Interchange.from_smirnoff(sage, off_topology) omm_topology = interchange.to_openmm_topology( @@ -824,7 +814,7 @@ def test_assign_unique_atom_names_some_duplicates(self, ensure_unique_atom_names # 1 unique O, and 6 unique Hs, for a total of 21 unique atom names assert len(atom_names) == 21 - def test_do_not_assign_unique_atom_names(self): + def test_do_not_assign_unique_atom_names(self, sage): """ Test disabling unique atom name assignment in Topology.to_openmm """ @@ -832,11 +822,12 @@ def test_do_not_assign_unique_atom_names(self): ethanol = Molecule.from_smiles("CCO") for atom in ethanol.atoms: atom.name = "eth_test" + benzene = Molecule.from_smiles("c1ccccc1") benzene.atoms[0].name = "bzn_test" + off_topology = Topology.from_molecules(molecules=[ethanol, benzene, benzene]) - sage = ForceField("openff-2.0.0.offxml") interchange = Interchange.from_smirnoff(sage, off_topology) omm_topology = interchange.to_openmm_topology(ensure_unique_atom_names=False) @@ -849,7 +840,7 @@ def test_do_not_assign_unique_atom_names(self): @pytest.mark.slow() @pytest.mark.parametrize("explicit_arg", [True, False]) - def test_preserve_per_residue_unique_atom_names(self, explicit_arg): + def test_preserve_per_residue_unique_atom_names(self, explicit_arg, sage): """ Test that to_openmm preserves atom names that are unique per-residue by default """ @@ -874,7 +865,6 @@ def test_preserve_per_residue_unique_atom_names(self, explicit_arg): # Record the initial atom names init_atomnames = [str(atom.name) for atom in off_topology.atoms] - sage = ForceField("openff-2.0.0.offxml") interchange = Interchange.from_smirnoff(sage, off_topology) # Perform the test @@ -891,7 +881,7 @@ def test_preserve_per_residue_unique_atom_names(self, explicit_arg): @pytest.mark.slow() @pytest.mark.parametrize("explicit_arg", [True, False]) - def test_generate_per_residue_unique_atom_names(self, explicit_arg): + def test_generate_per_residue_unique_atom_names(self, explicit_arg, sage): """ Test that to_openmm generates atom names that are unique per-residue """ @@ -922,7 +912,6 @@ def test_generate_per_residue_unique_atom_names(self, explicit_arg): ), f"Test assumes atom names are not unique per-residue in {res}" assert off_topology.n_atoms == 32, "Test assumes topology has 32 atoms" - sage = ForceField("openff-2.0.0.offxml") interchange = Interchange.from_smirnoff(sage, off_topology) # Perform the test @@ -951,6 +940,7 @@ def test_generate_per_residue_unique_atom_names(self, explicit_arg): def test_generate_per_molecule_unique_atom_names_with_residues( self, ensure_unique_atom_names, + sage, ): """ Test that to_openmm can generate atom names that are unique per-molecule @@ -983,7 +973,6 @@ def test_generate_per_molecule_unique_atom_names_with_residues( ), f"Test assumes atom names are not unique per-residue in {res}" assert off_topology.n_atoms == 32, "Test assumes topology has 32 atoms" - sage = ForceField("openff-2.0.0.offxml") interchange = Interchange.from_smirnoff(sage, off_topology) # Perform the test @@ -1003,7 +992,7 @@ def test_generate_per_molecule_unique_atom_names_with_residues( "ensure_unique_atom_names", [True, "residues", "chains", False], ) - def test_to_openmm_copies_molecules(self, ensure_unique_atom_names): + def test_to_openmm_copies_molecules(self, ensure_unique_atom_names, sage): """ Check that generating new atom names doesn't affect the input topology """ @@ -1020,7 +1009,6 @@ def test_to_openmm_copies_molecules(self, ensure_unique_atom_names): [mol._hierarchy_schemes for mol in off_topology.molecules], ), "Test assumes no hierarchy schemes" - sage = ForceField("openff-2.0.0.offxml") interchange = Interchange.from_smirnoff(sage, off_topology) # Record the initial atom names to compare to later @@ -1061,17 +1049,8 @@ def test_missing_positions(self): to_openmm_positions(Interchange()) @pytest.mark.parametrize("include_virtual_sites", [True, False]) - def test_positions_basic(self, include_virtual_sites): - force_field = ForceField( - "openff-2.0.0.offxml", - get_test_file_path( - "tip4p.offxml" if include_virtual_sites else "tip3p.offxml", - ), - ) - water = Molecule.from_smiles("O") - water.generate_conformers(n_conformers=1) - - out = Interchange.from_smirnoff(force_field, [water]) + def test_positions_basic(self, include_virtual_sites, water, tip4p): + out = Interchange.from_smirnoff(tip4p, [water]) positions = to_openmm_positions( out, @@ -1087,20 +1066,10 @@ def test_positions_basic(self, include_virtual_sites): ) @pytest.mark.parametrize("include_virtual_sites", [True, False]) - def test_given_positions(self, include_virtual_sites): + def test_given_positions(self, include_virtual_sites, water, tip4p): """Test issue #616""" - force_field = ForceField( - "openff-2.0.0.offxml", - get_test_file_path( - "tip4p.offxml" if include_virtual_sites else "tip3p.offxml", - ), - ) - - water = Molecule.from_smiles("O") - water.generate_conformers(n_conformers=1) - topology = Topology.from_molecules([water, water]) - out = Interchange.from_smirnoff(force_field, topology) + out = Interchange.from_smirnoff(tip4p, topology) # Approximate conformer position with a duplicate 5 A away in x out.positions = unit.Quantity( @@ -1134,41 +1103,30 @@ def test_given_positions(self, include_virtual_sites): class TestOpenMMToPDB(_BaseTest): - def test_to_pdb(self, sage): - import mdtraj as md + def test_to_pdb(self, sage, water): + import mdtraj - molecule = Molecule.from_smiles("O") + out = Interchange.from_smirnoff(sage, water.to_topology()) + out.to_pdb("out.pdb") - out = Interchange.from_smirnoff(sage, molecule.to_topology()) + mdtraj.load("out.pdb") + + out.positions = None with pytest.raises(MissingPositionsError): out.to_pdb("file_should_not_exist.pdb") - molecule.generate_conformers(n_conformers=1) - out.positions = molecule.conformers[0] - - out.to_pdb("out.pdb") - - md.load("out.pdb") - - with pytest.raises(UnsupportedExportError): - out.to_pdb("file_should_not_exist.pdb", writer="magik") - class TestBuckingham: - def test_water_with_virtual_sites(self): + def test_water_with_virtual_sites(self, water): force_field = ForceField( get_test_file_path("buckingham_virtual_sites.offxml"), load_plugins=True, ) - water = Molecule.from_mapped_smiles("[H:2][O:1][H:3]") - water.generate_conformers(n_conformers=1) - topology = water.to_topology() - interchange = Interchange.from_smirnoff( force_field=force_field, - topology=topology, + topology=water.to_topology(), box=[4, 4, 4], ) @@ -1236,53 +1194,27 @@ def test_water_with_virtual_sites(self): class TestGBSA(_BaseTest): - def test_create_gbsa(self): - force_field = ForceField( - "openff-2.0.0.offxml", - get_test_file_path("gbsa.offxml"), - ) - - molecule = Molecule.from_smiles("CCO") - molecule.generate_conformers(n_conformers=1) - + def test_create_gbsa(self, gbsa_force_field): interchange = Interchange.from_smirnoff( - force_field=force_field, - topology=molecule.to_topology(), + force_field=gbsa_force_field, + topology=MoleculeWithConformer.from_smiles("CCO").to_topology(), box=[4, 4, 4] * unit.nanometer, ) assert get_openmm_energies(interchange).total_energy is not None - def test_cannot_split_nonbonded_forces(self): - force_field = ForceField( - "openff-2.0.0.offxml", - get_test_file_path("gbsa.offxml"), - ) - - force_field["Electrostatics"] - molecule = Molecule.from_smiles("CCO") - molecule.generate_conformers(n_conformers=1) - + def test_cannot_split_nonbonded_forces(self, gbsa_force_field): with pytest.raises(UnsupportedExportError, match="exactly one"): Interchange.from_smirnoff( - force_field=force_field, - topology=molecule.to_topology(), + force_field=gbsa_force_field, + topology=MoleculeWithConformer.from_smiles("CCO").to_topology(), box=[4, 4, 4] * unit.nanometer, ).to_openmm(combine_nonbonded_forces=False) - def test_no_cutoff(self): - force_field = ForceField( - "openff-2.0.0.offxml", - get_test_file_path("gbsa.offxml"), - ) - - force_field["Electrostatics"] - molecule = Molecule.from_smiles("CCO") - molecule.generate_conformers(n_conformers=1) - + def test_no_cutoff(self, gbsa_force_field): system = Interchange.from_smirnoff( - force_field=force_field, - topology=molecule.to_topology(), + force_field=gbsa_force_field, + topology=MoleculeWithConformer.from_smiles("CCO").to_topology(), box=None, ).to_openmm(combine_nonbonded_forces=True) diff --git a/openff/interchange/_tests/unit_tests/components/test_interchange.py b/openff/interchange/_tests/unit_tests/components/test_interchange.py index 14e3fae10..4d8bea336 100644 --- a/openff/interchange/_tests/unit_tests/components/test_interchange.py +++ b/openff/interchange/_tests/unit_tests/components/test_interchange.py @@ -3,7 +3,6 @@ import numpy as np import pytest from openff.toolkit.topology import Molecule, Topology -from openff.toolkit.typing.engines.smirnoff import ForceField from openff.toolkit.typing.engines.smirnoff.parameters import ( ElectrostaticsHandler, ParameterHandler, @@ -68,21 +67,19 @@ def test_get_parameters(self, sage): with pytest.raises(MissingParametersError, match=r"atoms \(0, 100\)"): out._get_parameters("Bonds", (0, 100)) - def test_missing_electrostatics_handler(self, tip3p_missing_electrostatics_xml): + def test_missing_electrostatics_handler(self, tip3p, water): """Test that an error is raised when an electrostatics handler is missing""" - molecule = Molecule.from_smiles("O") - topology = Topology.from_molecules(molecule) - topology.box_vectors = unit.Quantity([4, 4, 4], units=unit.nanometer) + tip3p.deregister_parameter_handler("Electrostatics") - tip3p_missing_electrostatics = ForceField(tip3p_missing_electrostatics_xml) + topology = water.to_topology() + topology.box_vectors = unit.Quantity([4, 4, 4], units=unit.nanometer) with pytest.raises(MissingParameterHandlerError, match="modify partial"): - Interchange.from_smirnoff(tip3p_missing_electrostatics, topology) + Interchange.from_smirnoff(tip3p, topology) - tip3p = deepcopy(tip3p_missing_electrostatics) - - dummy_electrostatics_handler = ElectrostaticsHandler(skip_version_check=True) - tip3p.register_parameter_handler(dummy_electrostatics_handler) + tip3p.register_parameter_handler( + ElectrostaticsHandler(skip_version_check=True), + ) Interchange.from_smirnoff(tip3p, topology) diff --git a/openff/interchange/_tests/unit_tests/components/test_packmol.py b/openff/interchange/_tests/unit_tests/components/test_packmol.py index 0c32bad75..77b299d1a 100644 --- a/openff/interchange/_tests/unit_tests/components/test_packmol.py +++ b/openff/interchange/_tests/unit_tests/components/test_packmol.py @@ -19,8 +19,8 @@ @pytest.fixture(scope="module") -def molecules() -> list[Molecule]: - return [Molecule.from_smiles("O")] +def molecules(water) -> list[Molecule]: + return [water] @pytest.mark.parametrize( @@ -298,7 +298,7 @@ def test_pack_diatomic_ion(): ) -def test_solvate_structure(): +def test_solvate_structure(molecules): benzene = Molecule.from_smiles("c1ccccc1") with pytest.raises( @@ -306,7 +306,7 @@ def test_solvate_structure(): match="missing some atomic positions", ): pack_box( - [Molecule.from_smiles("O")], + molecules, [10], box_vectors=50 * numpy.identity(3) * unit.angstrom, solute=benzene.to_topology(), @@ -315,7 +315,7 @@ def test_solvate_structure(): benzene.generate_conformers(n_conformers=1) topology = pack_box( - [Molecule.from_smiles("O")], + molecules, [10], box_vectors=50 * numpy.identity(3) * unit.angstrom, solute=benzene.to_topology(), diff --git a/openff/interchange/_tests/unit_tests/components/test_toolkit.py b/openff/interchange/_tests/unit_tests/components/test_toolkit.py index 3b2abf4bb..2b4bf8dc6 100644 --- a/openff/interchange/_tests/unit_tests/components/test_toolkit.py +++ b/openff/interchange/_tests/unit_tests/components/test_toolkit.py @@ -1,5 +1,5 @@ import pytest -from openff.toolkit import ForceField, Molecule, Topology +from openff.toolkit import Molecule, Topology from openff.toolkit.topology._mm_molecule import _SimpleMolecule from openff.interchange._tests import _BaseTest @@ -18,8 +18,8 @@ def simple_methane(): @pytest.fixture() -def simple_water(): - return _SimpleMolecule.from_molecule(Molecule.from_smiles("O")) +def simple_water(water): + return _SimpleMolecule.from_molecule(water) def test_simple_topology_uniqueness(simple_methane, simple_water): @@ -53,15 +53,14 @@ def test_get_14_pairs(self, smiles, num_pairs): assert len([*_get_14_pairs(mol)]) == num_pairs assert len([*_get_14_pairs(mol.to_topology())]) == num_pairs - def test_check_electrostatics_handlers(self, tip3p_missing_electrostatics_xml): - # https://github.com/openforcefield/openff-toolkit/blob/0.10.2/openff/toolkit/data/test_forcefields/tip3p.offxml - tip3p_missing_electrostatics = ForceField(tip3p_missing_electrostatics_xml) + def test_check_electrostatics_handlers(self, tip3p): + tip3p.deregister_parameter_handler("Electrostatics") - assert _check_electrostatics_handlers(tip3p_missing_electrostatics) + assert _check_electrostatics_handlers(tip3p) - tip3p_missing_electrostatics.deregister_parameter_handler("LibraryCharges") + tip3p.deregister_parameter_handler("LibraryCharges") - assert not _check_electrostatics_handlers(tip3p_missing_electrostatics) + assert not _check_electrostatics_handlers(tip3p) @pytest.mark.parametrize( ("smiles", "num_h_bonds"), @@ -71,12 +70,11 @@ def test_get_num_h_bonds(self, smiles, num_h_bonds): topology = Molecule.from_smiles(smiles).to_topology() assert _get_num_h_bonds(topology) == num_h_bonds, smiles - def test_combine_topologies(self): + def test_combine_topologies(self, water): ethanol = Molecule.from_smiles("CCO") ethanol.name = "ETH" ethanol_topology = ethanol.to_topology() - water = Molecule.from_smiles("O") water.name = "WAT" water_topology = water.to_topology() diff --git a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py index 2ae027c34..2c2e813e6 100644 --- a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py +++ b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py @@ -30,16 +30,14 @@ def test_residue_names(self, sage): class TestSettles(_BaseTest): - def test_settles_units(self, monkeypatch): + def test_settles_units(self, monkeypatch, water): """Reproduce issue #720.""" monkeypatch.setenv("INTERCHANGE_EXPERIMENTAL", "1") - molecule = Molecule.from_mapped_smiles("[H:2][O:1][H:3]") - molecule.generate_conformers(n_conformers=1) - molecule.name = "WAT" + water.name = "WAT" ForceField("openff-2.1.0.offxml").create_interchange( - molecule.to_topology(), + water.to_topology(), ).to_gromacs( prefix="settles", ) diff --git a/openff/interchange/_tests/unit_tests/interop/gromacs/test_interchange.py b/openff/interchange/_tests/unit_tests/interop/gromacs/test_interchange.py index 1f7454fe3..d0252c884 100644 --- a/openff/interchange/_tests/unit_tests/interop/gromacs/test_interchange.py +++ b/openff/interchange/_tests/unit_tests/interop/gromacs/test_interchange.py @@ -70,10 +70,8 @@ def simple_system(self, sage_unconstrained) -> GROMACSSystem: return _convert(sage_unconstrained.create_interchange(topology)) @pytest.fixture() - def water_dimer(self, sage_unconstrained): - water = Molecule.from_mapped_smiles("[H:2][O:1][H:3]") + def water_dimer(self, sage_unconstrained, water): water.name = "WAT" - water.generate_conformers(n_conformers=1) topology = Topology.from_molecules([water, water]) topology.box_vectors = [4, 4, 4] * unit.nanometer diff --git a/openff/interchange/_tests/unit_tests/interop/test_openmm.py b/openff/interchange/_tests/unit_tests/interop/test_openmm.py index 11dc8d39b..f44a9639d 100644 --- a/openff/interchange/_tests/unit_tests/interop/test_openmm.py +++ b/openff/interchange/_tests/unit_tests/interop/test_openmm.py @@ -1,6 +1,7 @@ +from copy import deepcopy + import numpy import pytest -from openff.toolkit import ForceField, Molecule from openff.toolkit._tests.utils import get_14_scaling_factors from openff.units import unit from openff.units.openmm import ensure_quantity @@ -12,25 +13,25 @@ ) from openff.interchange import Interchange -from openff.interchange._tests import _BaseTest +from openff.interchange._tests import MoleculeWithConformer, _BaseTest class TestOpenMM(_BaseTest): - def test_no_nonbonded_force(self): + def test_no_nonbonded_force(self, sage): """ Ensure a SMIRNOFF-style force field can be exported to OpenMM even if no nonbonded handlers are present. For context, see https://github.com/openforcefield/openff-toolkit/issues/1102 """ - sage = ForceField("openff_unconstrained-2.0.0.offxml") + del sage._parameter_handlers["Constraints"] del sage._parameter_handlers["ToolkitAM1BCC"] del sage._parameter_handlers["LibraryCharges"] del sage._parameter_handlers["Electrostatics"] del sage._parameter_handlers["vdW"] - water = Molecule.from_smiles("C") + methane = MoleculeWithConformer.from_smiles("C") - openmm_system = Interchange.from_smirnoff(sage, [water]).to_openmm() + openmm_system = Interchange.from_smirnoff(sage, [methane]).to_openmm() for force in openmm_system.getForces(): if isinstance(force, NonbondedForce): @@ -44,18 +45,18 @@ def test_no_nonbonded_force(self): else: pytest.fail(f"Unexpected force found, type: {type(force)}") - def test_14_scale_factors_missing_electrostatics(self): + def test_14_scale_factors_missing_electrostatics(self, sage): # Ported from the toolkit after #1276 - top = Molecule.from_smiles("CCCC").to_topology() + topology = MoleculeWithConformer.from_smiles("CCCC").to_topology() - ff_no_electrostatics = ForceField("openff-2.0.0.offxml") + ff_no_electrostatics = deepcopy(sage) ff_no_electrostatics.deregister_parameter_handler("Electrostatics") ff_no_electrostatics.deregister_parameter_handler("ToolkitAM1BCC") ff_no_electrostatics.deregister_parameter_handler("LibraryCharges") out = Interchange.from_smirnoff( ff_no_electrostatics, - top, + topology, ).to_openmm(combine_nonbonded_forces=True) numpy.testing.assert_almost_equal( @@ -64,16 +65,16 @@ def test_14_scale_factors_missing_electrostatics(self): decimal=8, ) - def test_14_scale_factors_missing_vdw(self): + def test_14_scale_factors_missing_vdw(self, sage): # Ported from the toolkit after #1276 - top = Molecule.from_smiles("CCCC").to_topology() + topology = MoleculeWithConformer.from_smiles("CCCC").to_topology() - ff_no_vdw = ForceField("openff-2.0.0.offxml") + ff_no_vdw = deepcopy(sage) ff_no_vdw.deregister_parameter_handler("vdW") out = Interchange.from_smirnoff( ff_no_vdw, - top, + topology, ).to_openmm(combine_nonbonded_forces=True) numpy.testing.assert_almost_equal( @@ -86,43 +87,34 @@ def test_to_pdb_box_vectors(self, sage): """Reproduce https://github.com/openforcefield/openff-interchange/issues/548.""" from openmm.app import PDBFile - molecule = Molecule.from_smiles("CC") - molecule.generate_conformers(n_conformers=1) - box_vectors = unit.Quantity( + topology = MoleculeWithConformer.from_smiles("CC").to_topology() + topology.box_vectors = unit.Quantity( 10.0 * numpy.eye(3), unit.angstrom, ) - interchange = Interchange.from_smirnoff( - topology=[molecule], - force_field=sage, - box=box_vectors, - ) + interchange = Interchange.from_smirnoff(sage, topology) interchange.to_pdb("temp.pdb") parsed_box_vectors = PDBFile("temp.pdb").topology.getPeriodicBoxVectors() numpy.testing.assert_allclose( - box_vectors.m_as(unit.angstrom), + topology.box_vectors.m_as(unit.angstrom), ensure_quantity(parsed_box_vectors, "openff").m_as(unit.angstrom), ) class TestOpenMMMissingHandlers(_BaseTest): - def test_missing_vdw_combine_energies(self): + def test_missing_vdw_combine_energies(self, sage): from openff.interchange.drivers import get_openmm_energies - molecule = Molecule.from_smiles("CC") - molecule.generate_conformers(n_conformers=1) + topology = MoleculeWithConformer.from_smiles("CC").to_topology() - ff_no_vdw = ForceField("openff-2.0.0.offxml") + ff_no_vdw = deepcopy(sage) ff_no_vdw.deregister_parameter_handler("vdW") - out = Interchange.from_smirnoff( - ff_no_vdw, - [molecule], - ) + out = Interchange.from_smirnoff(ff_no_vdw, topology) energy1 = get_openmm_energies(out, combine_nonbonded_forces=True).total_energy energy2 = get_openmm_energies(out, combine_nonbonded_forces=False).total_energy diff --git a/openff/interchange/_tests/unit_tests/smirnoff/test_create.py b/openff/interchange/_tests/unit_tests/smirnoff/test_create.py index 416d9849e..2ac908363 100644 --- a/openff/interchange/_tests/unit_tests/smirnoff/test_create.py +++ b/openff/interchange/_tests/unit_tests/smirnoff/test_create.py @@ -1,8 +1,6 @@ import numpy import pytest -from openff.toolkit.topology import Molecule, Topology -from openff.toolkit.typing.engines.smirnoff.forcefield import ForceField -from openff.toolkit.typing.engines.smirnoff.parameters import LibraryChargeHandler +from openff.toolkit import ForceField, Molecule, Topology from openff.units import unit from openff.utilities.testing import skip_if_missing @@ -111,6 +109,8 @@ def test_catch_unassigned_torsions(self, sage, ethanol_top): # TODO: Remove xfail after openff-toolkit 0.10.0 @pytest.mark.xfail() def test_library_charges_from_molecule(): + from openff.toolkit.typing.engines.smirnoff.parameters import LibraryChargeHandler + mol = Molecule.from_mapped_smiles("[Cl:1][C:2]#[C:3][F:4]") with pytest.raises(ValueError, match="missing partial"): @@ -148,9 +148,9 @@ def test_charge_from_molecules_basic(self, sage): assert numpy.allclose(found_charges_uses, molecule.partial_charges.m) - def test_charges_on_molecules_in_topology(self, sage): + def test_charges_on_molecules_in_topology(self, sage, water): ethanol = Molecule.from_smiles("CCO") - water = Molecule.from_mapped_smiles("[H:2][O:1][H:3]") + ethanol_charges = numpy.linspace(-1, 1, 9) * 0.4 water_charges = numpy.linspace(-1, 1, 3) @@ -330,7 +330,7 @@ def test_setup_plugins(self): assert _PLUGIN_CLASS_MAPPING[BuckinghamHandler] == SMIRNOFFBuckinghamCollection - def test_create_buckingham(self): + def test_create_buckingham(self, water): force_field = ForceField( get_test_file_path("buckingham.offxml"), load_plugins=True, @@ -338,7 +338,7 @@ def test_create_buckingham(self): out = Interchange.from_smirnoff( force_field, - Molecule.from_smiles("O").to_topology(), + water.to_topology(), ) assert "Buckingham" in out.collections diff --git a/openff/interchange/_tests/unit_tests/smirnoff/test_valence.py b/openff/interchange/_tests/unit_tests/smirnoff/test_valence.py index a6f289f29..4e6e14393 100644 --- a/openff/interchange/_tests/unit_tests/smirnoff/test_valence.py +++ b/openff/interchange/_tests/unit_tests/smirnoff/test_valence.py @@ -33,7 +33,7 @@ class TestSMIRNOFFValenceCollections(_BaseTest): - def test_bond_collection(self): + def test_bond_collection(self, water): bond_handler = BondHandler(version=0.3) bond_handler.fractional_bondorder_method = "AM1-Wiberg" bond_parameter = BondHandler.BondType( @@ -48,7 +48,7 @@ def test_bond_collection(self): forcefield.register_parameter_handler(bond_handler) bond_potentials = SMIRNOFFBondCollection.create( parameter_handler=forcefield["Bonds"], - topology=Molecule.from_smiles("O").to_topology(), + topology=water.to_topology(), ) top_key = BondKey(atom_indices=(0, 1)) diff --git a/openff/interchange/_tests/unit_tests/smirnoff/test_virtual_sites.py b/openff/interchange/_tests/unit_tests/smirnoff/test_virtual_sites.py index 3ac5272b8..f5448aed2 100644 --- a/openff/interchange/_tests/unit_tests/smirnoff/test_virtual_sites.py +++ b/openff/interchange/_tests/unit_tests/smirnoff/test_virtual_sites.py @@ -3,8 +3,7 @@ import numpy import openmm import pytest -from openff.toolkit.topology import Molecule, Topology -from openff.toolkit.typing.engines.smirnoff.forcefield import ForceField +from openff.toolkit import ForceField, Molecule, Topology from openff.toolkit.typing.engines.smirnoff.parameters import ( ElectrostaticsHandler, LibraryChargeHandler, diff --git a/openff/interchange/components/interchange.py b/openff/interchange/components/interchange.py index ebf5b93fe..ff8dae3b9 100644 --- a/openff/interchange/components/interchange.py +++ b/openff/interchange/components/interchange.py @@ -256,8 +256,7 @@ def from_smirnoff( .. code-block:: pycon >>> from openff.interchange import Interchange - >>> from openff.toolkit.topology import Molecule - >>> from openff.toolkit.typing.engines.smirnoff import ForceField + >>> from openff.toolkit import ForceField, Molecule >>> mol = Molecule.from_smiles("CC") >>> mol.generate_conformers(n_conformers=1) >>> sage = ForceField("openff-2.0.0.offxml") diff --git a/openff/interchange/interop/openmm/_positions.py b/openff/interchange/interop/openmm/_positions.py index a3bba6178..745b4ca71 100644 --- a/openff/interchange/interop/openmm/_positions.py +++ b/openff/interchange/interop/openmm/_positions.py @@ -30,6 +30,8 @@ def to_openmm_positions( topology = interchange.topology + molecule_virtual_site_map = defaultdict(list) + if include_virtual_sites: from openff.interchange.interop._virtual_sites import ( _virtual_site_parent_molecule_mapping, @@ -37,8 +39,6 @@ def to_openmm_positions( virtual_site_molecule_map = _virtual_site_parent_molecule_mapping(interchange) - molecule_virtual_site_map = defaultdict(list) - for virtual_site, molecule_index in virtual_site_molecule_map.items(): molecule_virtual_site_map[molecule_index].append(virtual_site) diff --git a/openff/interchange/smirnoff/_base.py b/openff/interchange/smirnoff/_base.py index 81e9c126a..49e36a71e 100644 --- a/openff/interchange/smirnoff/_base.py +++ b/openff/interchange/smirnoff/_base.py @@ -300,6 +300,6 @@ def create( def __repr__(self) -> str: return ( - f"Handler '{self.type}' with expression '{self.expression}', {len(self.key_map)} slots, " + f"Handler '{self.type}' with expression '{self.expression}', {len(self.key_map)} mapping keys, " f"and {len(self.potentials)} potentials" ) diff --git a/openff/interchange/smirnoff/_valence.py b/openff/interchange/smirnoff/_valence.py index b453f57df..f8b2c2522 100644 --- a/openff/interchange/smirnoff/_valence.py +++ b/openff/interchange/smirnoff/_valence.py @@ -134,7 +134,7 @@ def store_matches( topology: Topology, ) -> None: """ - Populate self.key_map with key-val pairs of slots and unique potential identifiers. + Populate self.key_map with key-val pairs of keys and unique potential identifiers. """ if self.key_map: # TODO: Should the key_map always be reset, or should we be able to partially @@ -464,7 +464,7 @@ def store_matches( topology: Topology, ) -> None: """ - Populate self.key_map with key-val pairs of slots and unique potential identifiers. + Populate self.key_map with key-val pairs of keys and unique potential identifiers. """ if self.key_map: @@ -620,7 +620,7 @@ def store_matches( topology: Topology, ) -> None: """ - Populate self.key_map with key-val pairs of slots and unique potential identifiers. + Populate self.key_map with key-val pairs of keys and unique potential identifiers. """ if self.key_map: