Unverified Commit 0ae7458e authored by Peter Eastman's avatar Peter Eastman Committed by GitHub
Browse files

Fixed error adding vsites that depend on other vsites (#4898)

parent de180e4e
......@@ -1208,24 +1208,31 @@ class Modeller(object):
if site.index == index:
# This is a virtual site. Compute its position by the correct rule.
if site.type == 'average2':
position = site.weights[0]*templateAtomPositions[site.atoms[0]] + site.weights[1]*templateAtomPositions[site.atoms[1]]
elif site.type == 'average3':
position = site.weights[0]*templateAtomPositions[site.atoms[0]] + site.weights[1]*templateAtomPositions[site.atoms[1]] + site.weights[2]*templateAtomPositions[site.atoms[2]]
elif site.type == 'outOfPlane':
v1 = templateAtomPositions[site.atoms[1]] - templateAtomPositions[site.atoms[0]]
v2 = templateAtomPositions[site.atoms[2]] - templateAtomPositions[site.atoms[0]]
cross = Vec3(v1[1]*v2[2]-v1[2]*v2[1], v1[2]*v2[0]-v1[0]*v2[2], v1[0]*v2[1]-v1[1]*v2[0])
position = templateAtomPositions[site.atoms[0]] + site.weights[0]*v1 + site.weights[1]*v2 + site.weights[2]*cross
elif site.type == 'localCoords':
origin = unit.sum([templateAtomPositions[atom]*weight for atom, weight in zip(site.atoms, site.originWeights)])
xdir = unit.sum([templateAtomPositions[atom]*weight for atom, weight in zip(site.atoms, site.xWeights)])
ydir = unit.sum([templateAtomPositions[atom]*weight for atom, weight in zip(site.atoms, site.yWeights)])
zdir = Vec3(xdir[1]*ydir[2]-xdir[2]*ydir[1], xdir[2]*ydir[0]-xdir[0]*ydir[2], xdir[0]*ydir[1]-xdir[1]*ydir[0])
xdir /= norm(xdir);
zdir /= norm(zdir);
ydir = Vec3(zdir[1]*xdir[2]-zdir[2]*xdir[1], zdir[2]*xdir[0]-zdir[0]*xdir[2], zdir[0]*xdir[1]-zdir[1]*xdir[0])
position = origin + xdir*site.localPos[0] + ydir*site.localPos[1] + zdir*site.localPos[2];
try:
if site.type == 'average2':
position = site.weights[0]*templateAtomPositions[site.atoms[0]] + site.weights[1]*templateAtomPositions[site.atoms[1]]
elif site.type == 'average3':
position = site.weights[0]*templateAtomPositions[site.atoms[0]] + site.weights[1]*templateAtomPositions[site.atoms[1]] + site.weights[2]*templateAtomPositions[site.atoms[2]]
elif site.type == 'outOfPlane':
v1 = templateAtomPositions[site.atoms[1]] - templateAtomPositions[site.atoms[0]]
v2 = templateAtomPositions[site.atoms[2]] - templateAtomPositions[site.atoms[0]]
cross = Vec3(v1[1]*v2[2]-v1[2]*v2[1], v1[2]*v2[0]-v1[0]*v2[2], v1[0]*v2[1]-v1[1]*v2[0])
position = templateAtomPositions[site.atoms[0]] + site.weights[0]*v1 + site.weights[1]*v2 + site.weights[2]*cross
elif site.type == 'localCoords':
origin = unit.sum([templateAtomPositions[atom]*weight for atom, weight in zip(site.atoms, site.originWeights)])
xdir = unit.sum([templateAtomPositions[atom]*weight for atom, weight in zip(site.atoms, site.xWeights)])
ydir = unit.sum([templateAtomPositions[atom]*weight for atom, weight in zip(site.atoms, site.yWeights)])
zdir = Vec3(xdir[1]*ydir[2]-xdir[2]*ydir[1], xdir[2]*ydir[0]-xdir[0]*ydir[2], xdir[0]*ydir[1]-xdir[1]*ydir[0])
xdir /= norm(xdir)
zdir /= norm(zdir)
ydir = Vec3(zdir[1]*xdir[2]-zdir[2]*xdir[1], zdir[2]*xdir[0]-zdir[0]*xdir[2], zdir[0]*xdir[1]-zdir[1]*xdir[0])
position = origin + xdir*site.localPos[0] + ydir*site.localPos[1] + zdir*site.localPos[2]
except:
# This can happen if the virtual site depends on another virtual site whose position
# hasn't been set yet. Ignore the error. We'll put it at a random position (see below),
# which will get replaced with the correct position at the start of the simulation.
pass
if position is None and atom.type in drudeTypeMap:
# This is a Drude particle. Put it on top of its parent atom.
......
......@@ -1087,7 +1087,7 @@ class TestModeller(unittest.TestCase):
residue = topology.addResidue('Test', chain)
topology.addAtom('C', element.carbon, residue)
topology.addAtom('N', element.nitrogen, residue)
topology.addAtom('V', element.oxygen, residue)
topology.addAtom('O', element.oxygen, residue)
# Add the virtual sites.
......@@ -1115,6 +1115,54 @@ class TestModeller(unittest.TestCase):
self.assertVecAlmostEqual(p1.value_in_unit(nanometers), p2.value_in_unit(nanometers), 1e-6)
def testNestedVirtualSites(self):
"""Test adding virtual sites that depend on other virtual sites."""
xml = """
<ForceField>
<AtomTypes>
<Type name="C" class="C" element="C" mass="10"/>
<Type name="N" class="N" element="N" mass="10"/>
<Type name="V" class="V" mass="0.0"/>
</AtomTypes>
<Residues>
<Residue name="Test">
<Atom name="C" type="C"/>
<Atom name="N" type="N"/>
<Atom name="V1" type="V"/>
<Atom name="V2" type="V"/>
<VirtualSite type="average2" index="2" atom1="0" atom2="1" weight1="0.5" weight2="0.5"/>
<VirtualSite type="average2" index="3" atom1="0" atom2="2" weight1="0.5" weight2="0.5"/>
</Residue>
</Residues>
</ForceField>"""
ff = ForceField(StringIO(xml))
# Create the three real atoms.
topology = Topology()
chain = topology.addChain()
residue = topology.addResidue('Test', chain)
topology.addAtom('C', element.carbon, residue)
topology.addAtom('N', element.nitrogen, residue)
# Add the virtual sites.
modeller = Modeller(topology, [Vec3(0.0, 0.0, 0.0), Vec3(1.0, 0.0, 0.0)]*nanometers)
modeller.addExtraParticles(ff)
top = modeller.topology
pos = modeller.positions
# Check that the correct particles were added.
self.assertEqual(len(pos), 4)
for atom, elem in zip(top.atoms(), [element.carbon, element.nitrogen, None, None]):
self.assertEqual(elem, atom.element)
# The positions of the first virtual site should be correct, but the second one won't be.
self.assertVecAlmostEqual(Vec3(0.5, 0.0, 0.0), pos[2].value_in_unit(nanometers), 1e-6)
def test_multiSiteIon(self):
"""Test adding extra particles whose positions are determined based on bonds."""
xml = """
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment