Files
openmm/wrappers/python/tests/TestGromacsTopFile.py
2025-12-02 09:51:33 -08:00

318 lines
15 KiB
Python

import unittest
from validateConstraints import *
from openmm.app import *
from openmm import *
from openmm.unit import *
from openmm.app.gromacstopfile import _defaultGromacsIncludeDir
import openmm.app.element as elem
from numpy.testing import assert_allclose
GROMACS_INCLUDE = _defaultGromacsIncludeDir()
@unittest.skipIf(not os.path.exists(GROMACS_INCLUDE), 'GROMACS is not installed')
class TestGromacsTopFile(unittest.TestCase):
"""Test the GromacsTopFile.createSystem() method."""
def setUp(self):
"""Set up the tests by loading the input files."""
# alanine dipeptide with explicit water
self.top1 = GromacsTopFile('systems/explicit.top', unitCellDimensions=Vec3(6.223, 6.223, 6.223)*nanometers)
def test_NonbondedMethod(self):
"""Test all six options for the nonbondedMethod parameter."""
methodMap = {NoCutoff:NonbondedForce.NoCutoff,
CutoffNonPeriodic:NonbondedForce.CutoffNonPeriodic,
CutoffPeriodic:NonbondedForce.CutoffPeriodic,
Ewald:NonbondedForce.Ewald,
PME:NonbondedForce.PME,
LJPME:NonbondedForce.LJPME}
for method in methodMap:
system = self.top1.createSystem(nonbondedMethod=method)
forces = system.getForces()
self.assertTrue(any(isinstance(f, NonbondedForce) and
f.getNonbondedMethod()==methodMap[method]
for f in forces))
def test_LJPME_mixing(self):
"""Test that LJPME is not allowed with non-standard LJ mixing or NBFIX."""
tests = {
'apgr.nbfix.pairs.comb1.top': False,
'apgr.nbfix.pairs.comb2.top': False,
'apgr.nbfix.pairs.comb3.top': False,
'apgr.nbfix.nopairs.comb1.top': False,
'apgr.nbfix.nopairs.comb2.top': False,
'apgr.nbfix.nopairs.comb3.top': False,
'apgr.nonbfix.pairs.comb1.top': False,
'apgr.nonbfix.pairs.comb2.top': True,
'apgr.nonbfix.pairs.comb3.top': False,
'apgr.nonbfix.nopairs.comb1.top': False,
'apgr.nonbfix.nopairs.comb2.top': True,
'apgr.nonbfix.nopairs.comb3.top': False,
}
for topfile, ljpme_allowed in tests.items():
top = GromacsTopFile(f'systems/{topfile}', unitCellDimensions=Vec3(30, 30, 30)*angstroms)
if ljpme_allowed:
system = top.createSystem(nonbondedMethod=LJPME)
forces = system.getForces()
self.assertTrue(any(isinstance(f, NonbondedForce) and
f.getNonbondedMethod()==NonbondedForce.LJPME
for f in forces))
else:
with self.assertRaisesRegex(ValueError, 'LJPME is not supported'):
top.createSystem(nonbondedMethod=LJPME)
def test_ff99SBILDN(self):
""" Test Gromacs topology #define replacement as used in ff99SB-ILDN """
top = GromacsTopFile('systems/aidilnaaaaa.top')
gro = GromacsGroFile('systems/aidilnaaaaa.gro')
system = top.createSystem()
for force in system.getForces():
if isinstance(force, PeriodicTorsionForce):
force.setForceGroup(1)
context = Context(system, VerletIntegrator(1*femtosecond),
Platform.getPlatform('Reference'))
context.setPositions(gro.positions)
ene = context.getState(getEnergy=True, groups=2**1).getPotentialEnergy()
self.assertAlmostEqual(ene.value_in_unit(kilojoules_per_mole), 341.6905133582857)
def test_SMOG(self):
""" Test to ensure that SMOG models can be run without problems """
top = GromacsTopFile('systems/2ci2.pdb.top')
gro = GromacsGroFile('systems/2ci2.pdb.gro')
system = top.createSystem()
context = Context(system, VerletIntegrator(1*femtosecond),
Platform.getPlatform('Reference'))
context.setPositions(gro.positions)
ene = context.getState(getEnergy=True).getPotentialEnergy()
self.assertAlmostEqual(ene.value_in_unit(kilojoules_per_mole), -346.940915296)
def test_ionic(self):
"""Test simulating an ionic liquid"""
gro = GromacsGroFile('systems/ionic.gro')
top = GromacsTopFile('systems/ionic.top', periodicBoxVectors=gro.getPeriodicBoxVectors())
system = top.createSystem(nonbondedMethod=PME, nonbondedCutoff=1.2)
for f in system.getForces():
if isinstance(f, CustomNonbondedForce):
f.setUseLongRangeCorrection(True)
context = Context(system, VerletIntegrator(1*femtosecond),
Platform.getPlatform('Reference'))
context.setPositions(gro.positions)
energy = context.getState(getEnergy=True).getPotentialEnergy().value_in_unit(kilojoules_per_mole)
self.assertAlmostEqual(energy, 3135.33, delta=energy*0.005)
self.assertEqual(1400, system.getNumConstraints())
def test_Cutoff(self):
"""Test to make sure the nonbondedCutoff parameter is passed correctly."""
for method in [CutoffNonPeriodic, CutoffPeriodic, Ewald, PME, LJPME]:
system = self.top1.createSystem(nonbondedMethod=method,
nonbondedCutoff=2*nanometer,
constraints=HBonds)
cutoff_distance = 0.0*nanometer
cutoff_check = 2.0*nanometer
for force in system.getForces():
if isinstance(force, NonbondedForce):
cutoff_distance = force.getCutoffDistance()
self.assertEqual(cutoff_distance, cutoff_check)
def test_SwitchingFunction(self):
"""Test using a switching function."""
top = GromacsTopFile('systems/ionic.top')
for distance in (None, 0.8*nanometers):
system = top.createSystem(nonbondedMethod=CutoffNonPeriodic, switchDistance=distance)
for f in system.getForces():
if isinstance(f, NonbondedForce) or isinstance(f, CustomNonbondedForce):
if distance is None:
self.assertFalse(f.getUseSwitchingFunction())
else:
self.assertTrue(f.getUseSwitchingFunction())
self.assertEqual(distance, f.getSwitchingDistance())
def test_EwaldErrorTolerance(self):
"""Test to make sure the ewaldErrorTolerance parameter is passed correctly."""
for method in [Ewald, PME, LJPME]:
system = self.top1.createSystem(nonbondedMethod=method,
ewaldErrorTolerance=1e-6,
constraints=HBonds)
tolerance = 0
tolerance_check = 1e-6
for force in system.getForces():
if isinstance(force, NonbondedForce):
tolerance = force.getEwaldErrorTolerance()
self.assertEqual(tolerance, tolerance_check)
def test_RemoveCMMotion(self):
"""Test both options (True and False) for the removeCMMotion parameter."""
for b in [True, False]:
system = self.top1.createSystem(removeCMMotion=b)
self.assertEqual(any(isinstance(f, CMMotionRemover) for f in system.getForces()), b)
def test_RigidWaterAndConstraints(self):
"""Test all eight options for the constraints and rigidWater parameters."""
topology = self.top1.topology
for constraints_value in [None, HBonds, AllBonds, HAngles]:
for rigidWater_value in [True, False]:
system = self.top1.createSystem(constraints=constraints_value,
rigidWater=rigidWater_value)
validateConstraints(self, topology, system,
constraints_value, rigidWater_value)
def test_HydrogenMass(self):
"""Test that altering the mass of hydrogens works correctly."""
topology = self.top1.topology
hydrogenMass = 4*amu
system1 = self.top1.createSystem()
system2 = self.top1.createSystem(hydrogenMass=hydrogenMass)
for atom in topology.atoms():
if atom.element == elem.hydrogen:
self.assertNotEqual(hydrogenMass, system1.getParticleMass(atom.index))
if atom.residue.name == 'HOH':
self.assertEqual(system1.getParticleMass(atom.index), system2.getParticleMass(atom.index))
else:
self.assertEqual(hydrogenMass, system2.getParticleMass(atom.index))
totalMass1 = sum([system1.getParticleMass(i) for i in range(system1.getNumParticles())]).value_in_unit(amu)
totalMass2 = sum([system2.getParticleMass(i) for i in range(system2.getNumParticles())]).value_in_unit(amu)
self.assertAlmostEqual(totalMass1, totalMass2)
def test_VirtualParticle(self):
"""Test virtual particle works correctly."""
top = GromacsTopFile('systems/bnz.top')
gro = GromacsGroFile('systems/bnz.gro')
for atom in top.topology.atoms():
if atom.name.startswith('C'):
self.assertEqual(elem.carbon, atom.element)
elif atom.name.startswith('H'):
self.assertEqual(elem.hydrogen, atom.element)
else:
self.assertIsNone(atom.element)
system = top.createSystem()
self.assertEqual(26, system.getNumParticles())
self.assertEqual(1, len(top._moleculeTypes['BENX'].vsites2))
context = Context(system, VerletIntegrator(1*femtosecond),
Platform.getPlatform('Reference'))
context.setPositions(gro.positions)
context.computeVirtualSites()
ene = context.getState(getEnergy=True).getPotentialEnergy()
# the energy output is from gromacs and it only prints out 6 sig digits.
self.assertAlmostEqual(ene.value_in_unit(kilojoules_per_mole), 1.88855e+02, places=3)
def test_Vsite3Func1(self):
"""Test a three particle virtual site."""
top = GromacsTopFile('systems/tip4pew.top')
system = top.createSystem()
self.assertEqual(3, system.getNumConstraints())
self.assertTrue(system.isVirtualSite(3))
vs = system.getVirtualSite(3)
self.assertIsInstance(vs, ThreeParticleAverageSite)
self.assertEqual(0, vs.getParticle(0))
self.assertEqual(1, vs.getParticle(1))
self.assertEqual(2, vs.getParticle(2))
self.assertAlmostEqual(0.786646558, vs.getWeight(0))
self.assertAlmostEqual(0.106676721, vs.getWeight(1))
self.assertAlmostEqual(0.106676721, vs.getWeight(2))
def test_Vsite3Func3(self):
"""Test a three particle virtual site with function 3."""
top = GromacsTopFile('systems/vsite3-3.top')
gro = GromacsGroFile('systems/vsite3-3.gro')
system = top.createSystem()
context = Context(system, VerletIntegrator(1.0), Platform.getPlatform('Reference'))
context.setPositions(gro.positions)
context.computeVirtualSites()
positions = context.getState(positions=True).getPositions().value_in_unit(nanometer)
# Compare to the position computed by Gromacs
assert_allclose([2.45, 0.7794, 0.0], positions[3], atol=1e-4)
def test_Vsite3Func4(self):
"""Test a three particle virtual site."""
top = GromacsTopFile('systems/tip5p.top')
system = top.createSystem()
self.assertEqual(3, system.getNumConstraints())
for i in (3, 4):
self.assertTrue(system.isVirtualSite(i))
vs = system.getVirtualSite(i)
self.assertIsInstance(vs, OutOfPlaneSite)
self.assertEqual(0, vs.getParticle(0))
self.assertEqual(1, vs.getParticle(1))
self.assertEqual(2, vs.getParticle(2))
self.assertAlmostEqual(-0.344908, vs.getWeight12())
self.assertAlmostEqual(-0.344908, vs.getWeight13())
wc = -6.4437903493
if i == 4:
wc = -wc
self.assertAlmostEqual(wc, vs.getWeightCross())
def test_GROMOS(self):
"""Test a system using the GROMOS 54a7 force field."""
top = GromacsTopFile('systems/1ppt.top')
gro = GromacsGroFile('systems/1ppt.gro')
system = top.createSystem()
for i, f in enumerate(system.getForces()):
f.setForceGroup(i)
context = Context(system, VerletIntegrator(1*femtosecond), Platform.getPlatform('Reference'))
context.setPositions(gro.positions)
energy = {}
for i, f in enumerate(system.getForces()):
energy[f.getName()] = context.getState(getEnergy=True, groups={i}).getPotentialEnergy().value_in_unit(kilojoules_per_mole)
# Compare to energies computed with GROMACS.
assert_allclose(1.12797e+03, energy['GROMOSBondForce'], rtol=1e-4)
assert_allclose(5.59066e+02, energy['GROMOSAngleForce'], rtol=1e-4)
assert_allclose(3.80152e+02, energy['PeriodicTorsionForce'], rtol=1e-4)
assert_allclose(9.59178e+01, energy['HarmonicTorsionForce'], rtol=1e-4)
assert_allclose(2.75307e+02, energy['LennardJonesExceptions'], rtol=1e-4)
assert_allclose(-7.53704e+02, energy['LennardJonesForce'], rtol=1e-4)
assert_allclose(-6.23055e+03+4.36880e+03, energy['NonbondedForce'], rtol=1e-4)
total = context.getState(getEnergy=True).getPotentialEnergy().value_in_unit(kilojoules_per_mole)
assert_allclose(-1.77020e+02, total, rtol=1e-3)
def test_NBFIX(self):
"""Test systems using NBFIX with and without pairtypes and different combination rules."""
gro = GromacsGroFile('systems/apgr.gro')
energies = {
'apgr.nbfix.pairs.comb1.top': -986.713,
'apgr.nbfix.pairs.comb2.top': -986.485,
'apgr.nbfix.pairs.comb3.top': -986.713,
'apgr.nbfix.nopairs.comb1.top': -912.181,
'apgr.nbfix.nopairs.comb2.top': -903.437,
'apgr.nbfix.nopairs.comb3.top': -912.181,
'apgr.nonbfix.pairs.comb1.top': -993.104,
'apgr.nonbfix.pairs.comb2.top': -992.685,
'apgr.nonbfix.pairs.comb3.top': -993.104,
'apgr.nonbfix.nopairs.comb1.top': -918.572,
'apgr.nonbfix.nopairs.comb2.top': -909.637,
'apgr.nonbfix.nopairs.comb3.top': -918.572,
'apgr.nbfix.nopairs.comb2.fudge.top': -856.721,
'apgr.nbfix14.nopairs.comb2.top': -909.755
}
for topfile, expected in energies.items():
top = GromacsTopFile(f'systems/{topfile}')
system = top.createSystem(nonbondedMethod=NoCutoff,switchDistance=None,constraints=None,useDispersionCorrection=False)
context = Context(system, VerletIntegrator(1*femtosecond), Platform.getPlatform('Reference'))
context.setPositions(gro.positions)
energy=context.getState(getEnergy=True).getPotentialEnergy().value_in_unit(kilojoules_per_mole)
assert_allclose(energy, expected, rtol=1E-6)
if __name__ == '__main__':
unittest.main()