Skip to content

Commit

Permalink
Merge pull request #11 from mattwthompson/energy-tests
Browse files Browse the repository at this point in the history
Update energy tests
  • Loading branch information
jthorton authored Mar 8, 2024
2 parents 8e7d428 + c9e2fbd commit eb99f5e
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 9 deletions.
26 changes: 26 additions & 0 deletions deforcefields/data/ethanol.sdf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

-OEChem-02242314133D

9 8 0 0 0 0 0 0 0999 V2000
1.0616 -0.2681 -0.0006 C 0 0 0 0 0 0 0 0 0 0 0 0
1.9101 0.9126 -0.4220 C 0 0 0 0 0 0 0 0 0 0 0 0
1.5170 1.3236 -1.7228 O 0 0 0 0 0 0 0 0 0 0 0 0
1.3393 -0.6108 1.0001 H 0 0 0 0 0 0 0 0 0 0 0 0
-0.0000 -0.0002 -0.0006 H 0 0 0 0 0 0 0 0 0 0 0 0
1.1801 -1.0990 -0.7040 H 0 0 0 0 0 0 0 0 0 0 0 0
2.9684 0.6361 -0.4446 H 0 0 0 0 0 0 0 0 0 0 0 0
1.7730 1.7499 0.2687 H 0 0 0 0 0 0 0 0 0 0 0 0
2.0787 2.0794 -1.9612 H 0 0 0 0 0 0 0 0 0 0 0 0
1 2 1 0 0 0 0
2 3 1 0 0 0 0
1 4 1 0 0 0 0
1 5 1 0 0 0 0
1 6 1 0 0 0 0
2 7 1 0 0 0 0
2 8 1 0 0 0 0
3 9 1 0 0 0 0
M END
> <atom.dprop.PartialCharge>
-0.097100 0.131430 -0.601340 0.044760 0.044760 0.044760 0.017320 0.017320 0.398090

$$$$
11 changes: 11 additions & 0 deletions deforcefields/data/water.sdf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

-OEChem-02242314343D

3 2 0 0 0 0 0 0 0999 V2000
0.0611 0.3887 0.0589 O 0 0 0 0 0 0 0 0 0 0 0 0
0.7238 -0.3116 -0.0382 H 0 0 0 0 0 0 0 0 0 0 0 0
-0.7849 -0.0770 -0.0207 H 0 0 0 0 0 0 0 0 0 0 0 0
1 2 1 0 0 0 0
1 3 1 0 0 0 0
M END
$$$$
41 changes: 41 additions & 0 deletions deforcefields/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import os.path

import pytest
from openff.toolkit.topology import Molecule


def get_data(relative_path: str) -> str:
"""
Get the file path to some data in the package for testing.
Args:
relative_path: The relative path of the file to be loaded from deforcefields/data
Returns:
The absolute path to the requested file in deforcefields/data.
"""
from pkg_resources import resource_filename

file_name = resource_filename("deforcefields", os.path.join("data", relative_path))
if not os.path.exists(file_name):
raise ValueError(
f"{relative_path} does not exist. If you have just added it, you'll have to re-install."
)
return file_name


@pytest.fixture()
def ethanol_with_charges() -> Molecule:
"""
Return and OpenFF Molecule model of ethanol with `am1bccelf10` charges computed with openeye
"""
ethanol = Molecule.from_file(get_data("ethanol.sdf"))
return ethanol


@pytest.fixture()
def water() -> Molecule:
"""
Return an OpenFF Molecule model of water with no charges.
"""
return Molecule.from_file(get_data("water.sdf"))
98 changes: 92 additions & 6 deletions deforcefields/tests/test_deforcefields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
Test loading DE-Force fields via the plugin interface through the toolkit.
"""

import numpy
import openmm
import pytest
from openff.interchange.constants import kj_mol
from openff.interchange.drivers.openmm import get_openmm_energies
from openff.toolkit import ForceField, Molecule
from openff.toolkit import ForceField, Molecule, Quantity, Topology
from openmm import unit


Expand All @@ -16,17 +18,16 @@
pytest.param("de-force_unconstrained-1.0.2.offxml", id="Constraints"),
],
)
def test_load_de_ff(forcefield):
def test_load_de_ff(forcefield, ethanol_with_charges):
"""
Load the DE FF and create an OpenMM system.
"""

ff = ForceField(forcefield, load_plugins=True)
ethanol = Molecule.from_smiles("CCO")

system = ff.create_interchange(topology=ethanol.to_topology()).to_openmm(
combine_nonbonded_forces=False,
)
system = ff.create_interchange(
topology=ethanol_with_charges.to_topology(),
).to_openmm(combine_nonbonded_forces=False)

forces = {force.__class__.__name__: force for force in system.getForces()}

Expand Down Expand Up @@ -89,3 +90,88 @@ def test_fails_unsupported_chemistry():
).create_interchange(
Molecule.from_mapped_smiles("[H:3][C:1]#[C:2][H:4]").to_topology()
).to_openmm(combine_nonbonded_forces=False)


@pytest.mark.parametrize(
"forcefield, ref_energy",
[
pytest.param("de-force-1.0.2.offxml", 13.601144438830156, id="No constraints"),
pytest.param(
"de-force_unconstrained-1.0.2.offxml", 13.605201859835375, id="Constraints"
),
],
)
def test_energy_no_sites(forcefield, ref_energy, ethanol_with_charges):
"""
Test calculating the single point energy of ethanol using constrained and unconstrained DE-FF with pre-computed
partial charges from openeye.
"""

ff = ForceField(forcefield, load_plugins=True)

interchange = ff.create_interchange(
topology=ethanol_with_charges.to_topology(),
charge_from_molecules=[ethanol_with_charges],
)

found_energy = get_openmm_energies(
interchange,
combine_nonbonded_forces=False,
).total_energy.m_as(kj_mol)

assert found_energy == pytest.approx(ref_energy)


def evaluate_water_energy_at_distance(
force_field: ForceField,
distance: float,
) -> list[Quantity]:
"""
Evaluate a water dimer at specified distances (in Angstrom).
Taken from smirnoff_plugins.utilities.openmm, which collates virtual particles
between molecules. Interchange (with OpenMM) puts all virtual sites at the END
of the topology; mismatching these causes NaNs.
"""

water = Molecule.from_smiles("O")
water.generate_conformers(n_conformers=1)
topology = Topology.from_molecules([water, water])
topology.box_vectors = unit.Quantity(numpy.eye(3) * 20, unit.nanometer)

topology.set_positions(
numpy.vstack(
[
water.conformers[0],
water.conformers[0]
+ Quantity(
numpy.array([distance, 0, 0]),
"angstrom",
),
],
),
)

return get_openmm_energies(
force_field.create_interchange(topology),
combine_nonbonded_forces=False,
).total_energy


@pytest.mark.parametrize(
("distance", "ref_energy"),
[(2, 1005.0846252441406), (3, 44.696786403656006), (4, 10.453390896320343)],
)
def test_energy_sites(distance, ref_energy):
"""
Test calculating the energy for a system with two waters with virtual sites at set distances.
"""

ff = ForceField("de-force-1.0.2.offxml", load_plugins=True)

found_energy = evaluate_water_energy_at_distance(
force_field=ff,
distance=distance,
)

assert found_energy.m_as(kj_mol) == pytest.approx(ref_energy, abs=0.01)
6 changes: 3 additions & 3 deletions devtools/conda-envs/test_env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ dependencies:
- pytest

# Core-deps
- openff-toolkit >=0.14.3
- openff-interchange >=0.3.18
- openff-toolkit >=0.15
- openff-interchange >=0.3.23
# the above two constraints are pulled in by smirnoff-plugins;
# could just drop them, but maybe it's better to be explicit
- smirnoff-plugins >=2023.08.0
- smirnoff-plugins >=2024.01.0

0 comments on commit eb99f5e

Please sign in to comment.