Skip to content

Commit

Permalink
TST: Add first pass of tests for handling virtual sites in from_openmm
Browse files Browse the repository at this point in the history
  • Loading branch information
mattwthompson committed Oct 21, 2024
1 parent dca833b commit 0976c2b
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 11 deletions.
13 changes: 10 additions & 3 deletions openff/interchange/_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,28 @@


@pytest.fixture
def sage():
def sage() -> ForceField:
return ForceField("openff-2.0.0.offxml")


@pytest.fixture
def sage_unconstrained():
def sage_unconstrained() -> ForceField:
return ForceField("openff_unconstrained-2.0.0.offxml")


@pytest.fixture
def sage_no_switch(sage):
def sage_no_switch(sage) -> ForceField:
sage["vdW"].switch_width = Quantity(0.0, "angstrom")
return sage


@pytest.fixture
def sage_with_tip4p() -> ForceField:
# re-build off of existing fixtures if this gets implemented
# https:/openforcefield/openff-toolkit/issues/1948
return ForceField("openff-2.0.0.offxml", "tip4p.offxml")


@pytest.fixture
def sage_with_bond_charge(sage):
sage["Bonds"].add_parameter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@

class TestUnsupportedCases:
@pytest.mark.filterwarnings("ignore:.*are you sure you don't want to pass positions")
def test_error_topology_mismatch(self, monkeypatch, sage_unconstrained, ethanol):
monkeypatch.setenv("INTERCHANGE_EXPERIMENTAL", "1")

def test_error_topology_mismatch(self, sage_unconstrained, ethanol):
topology = ethanol.to_topology()
topology.box_vectors = Quantity([4, 4, 4], "nanometer")

Expand All @@ -38,23 +36,39 @@ def test_error_topology_mismatch(self, monkeypatch, sage_unconstrained, ethanol)
topology=other_topology.to_openmm(),
)

def test_found_virtual_sites(self, monkeypatch, tip4p, water):
monkeypatch.setenv("INTERCHANGE_EXPERIMENTAL", "1")

def test_found_out_of_plane_virtual_site(self, tip5p, water):
topology = water.to_topology()
topology.box_vectors = Quantity([4, 4, 4], "nanometer")

system = tip4p.create_openmm_system(topology)
system = tip5p.create_openmm_system(topology)

with pytest.raises(
UnsupportedImportError,
match="A particle is a virtual site, which is not yet supported.",
match="A particle is an `outOfPlane` virtual site, which is not yet supported.",
):
from_openmm(
system=system,
topology=topology.to_openmm(),
)

def test_found_two_particle_average_virtual_site(
self,
sage_with_bond_charge,
default_integrator,
):
simulation = sage_with_bond_charge.create_interchange(
Molecule.from_smiles("CCl").to_topology(),
).to_openmm_simulation(integrator=default_integrator)

with pytest.raises(
UnsupportedImportError,
match="A particle is a `TwoParticleAverage` virtual site, which is not yet supported.",
):
from_openmm(
system=simulation.system,
topology=simulation.topology,
)

def test_missing_positions_warning(self, monkeypatch, sage, water):
monkeypatch.setenv("INTERCHANGE_EXPERIMENTAL", "1")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import pytest
from openff.toolkit import Topology

from openff.interchange import Interchange
from openff.interchange.components._packmol import solvate_topology
from openff.interchange.drivers.openmm import _get_openmm_energies, get_openmm_energies


class TestTIP4PVirtualSites:
def test_dimer_energy_equals(self, tip4p, water_dimer):
out: Interchange = tip4p.create_interchange(water_dimer)

roundtripped = Interchange.from_openmm(
system=out.to_openmm_system(),
topology=out.to_openmm_topology(collate=False),
positions=out.get_positions(include_virtual_sites=True).to_openmm(),
box_vectors=out.box.to_openmm(),
)

assert get_openmm_energies(out) == _get_openmm_energies(roundtripped)

def test_minimize_solvated_ligand(self, sage_with_tip4p, ethanol, default_integrator):
topology = solvate_topology(ethanol.to_topology())

simulation = sage_with_tip4p.create_simulation(
topology,
).to_openmm_simulation(
integrator=default_integrator,
)

roundtripped = Interchange.from_openmm(
system=simulation.system,
topology=simulation.topology,
positions=simulation.context.getState(getPositions=True).getPositions(),
box_vectors=simulation.system.getDefaultPeriodicBoxVectors(),
)

original_energy = get_openmm_energies(simulation)

# TODO: Much more validation could be done here, but if a simulation
# can start and minimize at all, that should catch most problems
roundtripped.minimize()

assert get_openmm_energies(roundtripped) < original_energy

def test_error_index_mismatch(self, tip4p, water):
out: Interchange = tip4p.create_interchange(Topology.from_molecules([water, water]))

with pytest.raises(
ValueError, # TODO: Make a different error
match="The number of particles in the system and the number of atoms in the topology do not match.",
):
Interchange.from_openmm(
system=out.to_openmm_system(),
topology=out.to_openmm_topology(collate=True),
)

0 comments on commit 0976c2b

Please sign in to comment.