Skip to content

Commit 02058c2

Browse files
Merge pull request #906 from openforcefield/virtual-site-virtual-site-exclusions
Add (virtual site)-to-(virtual site) exceptions
2 parents f69816c + 36f54b5 commit 02058c2

File tree

6 files changed

+385
-149
lines changed

6 files changed

+385
-149
lines changed

docs/releasehistory.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ Dates are given in YYYY-MM-DD format.
1111

1212
Please note that all releases prior to a version 1.0.0 are considered pre-releases and many API changes will come before a stable release.
1313

14+
## 0.3.21 - 2023-02-20
15+
16+
* #906 Fixes a bug in which intramolecular interactions between virtual sites were not properly excluded with OpenMM.
17+
* #901 `Interchange.from_openmm` now requires the `system` argument.
18+
* #903 The Python API of LAMMPS is now internally used for LAMMPS energy calculations.
19+
1420
## 0.3.20 - 2023-02-12
1521

1622
* #891 Adds support for hydrogen mass repartitioning (HMR) in GROMACS export. Note that this implementaiton never modifies masses in waters and requires the system contains no virtual sites.

openff/interchange/_tests/conftest.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,33 @@ def sage_with_trivalent_nitrogen():
101101
return sage_210
102102

103103

104+
@pytest.fixture()
105+
def sage_with_off_center_hydrogen(sage):
106+
virtual_sites = sage.get_parameter_handler("VirtualSites")
107+
108+
# Add a virtual site for an off-center hydrogen, see issue #905
109+
# this differs by JH's example by adding an arbitrary charge increment in
110+
# order to test the electrostatics of virtual site pairs that interact
111+
# by 1-4 interactions (as determined by their parent relationship)
112+
virtual_sites.add_parameter(
113+
parameter_kwargs={
114+
"smirks": "[#1:1]~[*:2]",
115+
"epsilon": Quantity(0.0157, "kilocalorie / mole"),
116+
"type": "BondCharge",
117+
"match": "all_permutations",
118+
"distance": Quantity(-0.1, "angstrom"),
119+
"outOfPlaneAngle": None,
120+
"inPlaneAngle": None,
121+
"charge_increment1": Quantity(0.12345, "elementary_charge"),
122+
"charge_increment2": Quantity(0, "elementary_charge"),
123+
"sigma": Quantity(1.069078461768407, "angstrom"),
124+
"name": "OFF_SITE_HYDROGEN",
125+
},
126+
)
127+
128+
return sage
129+
130+
104131
@pytest.fixture()
105132
def _simple_force_field():
106133
# TODO: Create a minimal force field for faster tests

openff/interchange/_tests/interoperability_tests/test_openmm.py

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -569,78 +569,6 @@ def test_valence_term_paticle_index_offsets(self, water, tip5p):
569569
assert isinstance(out.getVirtualSite(index), openmm.VirtualSite)
570570

571571

572-
@skip_if_missing("openmm")
573-
class TestOpenMMVirtualSiteExclusions:
574-
def test_tip5p_num_exceptions(self, water):
575-
tip5p = ForceField(get_test_file_path("tip5p.offxml"))
576-
577-
out = Interchange.from_smirnoff(tip5p, [water]).to_openmm(
578-
combine_nonbonded_forces=True,
579-
)
580-
581-
# In a TIP5P water expected exceptions include (total 10)
582-
#
583-
# V(3) V(4) Oxygen to hydrogens and particles (4)
584-
# \ / - (0, 1), (0, 2), (0, 3), (0, 4)
585-
# O(0) Hyrogens to virtual particles (4)
586-
# / \ - (1, 3), (1, 4), (2, 3), (2, 4)
587-
# H(1) H(2) Hydrogens and virtual particles to each other (2)
588-
# - (1, 2), (3, 4)
589-
590-
for force in out.getForces():
591-
if isinstance(force, openmm.NonbondedForce):
592-
assert force.getNumExceptions() == 10
593-
594-
def test_dichloroethane_exceptions(self, sage):
595-
"""Test a case in which a parent's 1-4 exceptions must be 'imported'."""
596-
from openff.toolkit._tests.mocking import VirtualSiteMocking
597-
598-
# This molecule has heavy atoms with indices (1-indexed) CL1, C2, C3, Cl4,
599-
# resulting in 1-4 interactions between the Cl-Cl pair and some Cl-H pairs
600-
dichloroethane = Molecule.from_mapped_smiles(
601-
"[Cl:1][C:2]([H:5])([H:6])[C:3]([H:7])([H:8])[Cl:4]",
602-
)
603-
604-
# This parameter pulls 0.1 and 0.2e from Cl (parent) and C, respectively, and has
605-
# LJ parameters of 4 A, 3 kJ/mol
606-
parameter = VirtualSiteMocking.bond_charge_parameter("[Cl:1]-[C:2]")
607-
608-
handler = VirtualSiteHandler(version="0.3")
609-
handler.add_parameter(parameter=parameter)
610-
611-
sage.register_parameter_handler(handler)
612-
613-
system = Interchange.from_smirnoff(sage, [dichloroethane]).to_openmm(
614-
combine_nonbonded_forces=True,
615-
)
616-
617-
assert system.isVirtualSite(8)
618-
assert system.isVirtualSite(9)
619-
620-
non_bonded_force = [
621-
f for f in system.getForces() if isinstance(f, openmm.NonbondedForce)
622-
][0]
623-
624-
for exception_index in range(non_bonded_force.getNumExceptions()):
625-
p1, p2, q, sigma, epsilon = non_bonded_force.getExceptionParameters(
626-
exception_index,
627-
)
628-
if p2 == 8:
629-
# Parent Cl, adjacent C and its bonded H, and the 1-3 C
630-
if p1 in (0, 1, 2, 4, 5):
631-
assert q._value == epsilon._value == 0.0
632-
# 1-4 Cl or 1-4 Hs
633-
if p1 in (3, 6, 7):
634-
for value in (q, sigma, epsilon):
635-
assert value._value != 0, (q, sigma, epsilon)
636-
if p2 == 9:
637-
if p1 in (3, 1, 2, 6, 7):
638-
assert q._value == epsilon._value == 0.0
639-
if p1 in (0, 4, 5):
640-
for value in (q, sigma, epsilon):
641-
assert value._value != 0, (q, sigma, epsilon)
642-
643-
644572
@skip_if_missing("openmm")
645573
class TestToOpenMMTopology:
646574
def test_num_virtual_sites(self, water, tip4p):

0 commit comments

Comments
 (0)