Commit 8df54762 authored by Lee-Ping Wang's avatar Lee-Ping Wang
Browse files

Merge branch 'master' of github.com:leeping/openmm

parents 3cb25ad8 59854c5e
...@@ -41,9 +41,9 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -41,9 +41,9 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
import os import os
import os.path import os.path
import copy
import re import re
import math import math
import warnings
try: try:
import numpy import numpy
...@@ -52,6 +52,7 @@ except: ...@@ -52,6 +52,7 @@ except:
import simtk.unit as units import simtk.unit as units
import simtk.openmm import simtk.openmm
from simtk.openmm.app import element as elem
import customgbforces as customgb import customgbforces as customgb
#============================================================================================= #=============================================================================================
...@@ -428,15 +429,25 @@ class PrmtopLoader(object): ...@@ -428,15 +429,25 @@ class PrmtopLoader(object):
charges=self.getCharges() charges=self.getCharges()
nonbondTerms = self.getNonbondTerms() nonbondTerms = self.getNonbondTerms()
for ii in range(0,len(dihedralPointers),5): for ii in range(0,len(dihedralPointers),5):
if int(dihedralPointers[ii+2])>0 and int(dihedralPointers[ii+3])>0: if int(dihedralPointers[ii+2])>0 and int(dihedralPointers[ii+3])>0:
iAtom = int(dihedralPointers[ii])//3 iAtom = int(dihedralPointers[ii])//3
lAtom = int(dihedralPointers[ii+3])//3 lAtom = int(dihedralPointers[ii+3])//3
chargeProd = charges[iAtom]*charges[lAtom] iidx = int(dihedralPointers[ii+4]) - 1
(rVdwI, epsilonI) = nonbondTerms[iAtom] chargeProd = charges[iAtom]*charges[lAtom]
(rVdwL, epsilonL) = nonbondTerms[lAtom] (rVdwI, epsilonI) = nonbondTerms[iAtom]
rMin = (rVdwI+rVdwL) (rVdwL, epsilonL) = nonbondTerms[lAtom]
epsilon = math.sqrt(epsilonI*epsilonL) rMin = (rVdwI+rVdwL)
returnList.append((iAtom, lAtom, chargeProd, rMin, epsilon)) epsilon = math.sqrt(epsilonI*epsilonL)
try:
iScee = float(self._raw_data["SCEE_SCALE_FACTOR"][iidx])
except KeyError:
iScee = 1.2
try:
iScnb = float(self._raw_data["SCNB_SCALE_FACTOR"][iidx])
except KeyError:
iScnb = 2.0
returnList.append((iAtom, lAtom, chargeProd, rMin, epsilon, iScee, iScnb))
return returnList return returnList
def getExcludedAtoms(self): def getExcludedAtoms(self):
...@@ -462,34 +473,74 @@ class PrmtopLoader(object): ...@@ -462,34 +473,74 @@ class PrmtopLoader(object):
self._excludedAtoms.append(atomList) self._excludedAtoms.append(atomList)
return self._excludedAtoms return self._excludedAtoms
def getGBParms(self, symbls=None): def getGBParms(self, gbmodel, elements):
"""Return list giving GB params, Radius and screening factor""" """Return list giving GB params, Radius and screening factor"""
try: gb_List=[]
return self._gb_List radii=[float(r) for r in self._raw_data["RADII"]]
except AttributeError: screen=[float(s) for s in self._raw_data["SCREEN"]]
pass if gbmodel == 'GBn2':
self._gb_List=[] alpha = [0 for i in self._raw_data['RADII']]
radii=self._raw_data["RADII"] beta = [0 for i in self._raw_data['RADII']]
screen=self._raw_data["SCREEN"] gamma = [0 for i in self._raw_data['RADII']]
# Update screening parameters for GBn if specified # Update screening parameters for GBn if specified
if symbls: if gbmodel == 'GBn':
for (i, symbl) in enumerate(symbls): if elements is None:
if symbl[0] == ('c' or 'C'): raise Exception('GBn model requires element information')
for i, element in enumerate(elements):
if element is elem.carbon:
screen[i] = 0.48435382330 screen[i] = 0.48435382330
elif symbl[0] == ('h' or 'H'): elif element is elem.hydrogen:
screen[i] = 1.09085413633 screen[i] = 1.09085413633
elif symbl[0] == ('n' or 'N'): elif element is elem.nitrogen:
screen[i] = 0.700147318409 screen[i] = 0.700147318409
elif symbl[0] == ('o' or 'O'): elif element is elem.oxygen:
screen[i] = 1.06557401132 screen[i] = 1.06557401132
elif symbl[0] == ('s' or 'S'): elif element is elem.sulfur:
screen[i] = 0.602256336067 screen[i] = 0.602256336067
else: else:
screen[i] = 0.5 screen[i] = 0.5
if gbmodel == 'GBn2':
if elements is None:
raise Exception('GBn2 model requires element information')
for i, element in enumerate(elements):
if element is elem.carbon:
screen[i] = 1.058554
alpha[i] = 0.733756
beta[i] = 0.506378
gamma[i] = 0.205844
elif element is elem.hydrogen:
screen[i] = 1.425952
alpha[i] = 0.788440
beta[i] = 0.798699
gamma[i] = 0.437334
elif element is elem.nitrogen:
screen[i] = 0.733599
alpha[i] = 0.503364
beta[i] = 0.316828
gamma[i] = 0.192915
elif element is elem.oxygen:
screen[i] = 1.061039
alpha[i] = 0.867814
beta[i] = 0.876635
gamma[i] = 0.387882
elif element is elem.sulfur:
screen[i] = -0.703469
alpha[i] = 0.867814
beta[i] = 0.876635
gamma[i] = 0.387882
else: # not optimized
screen[i] = 0.5
alpha[i] = 1.0
beta[i] = 0.8
gamma[i] = 4.85
lengthConversionFactor = units.angstrom.conversion_factor_to(units.nanometer) lengthConversionFactor = units.angstrom.conversion_factor_to(units.nanometer)
for iAtom in range(len(radii)): if gbmodel == 'GBn2':
self._gb_List.append((float(radii[iAtom])*lengthConversionFactor, float(screen[iAtom]))) for rad, scr, alp, bet, gam in zip(radii, screen, alpha, beta, gamma):
return self._gb_List gb_List.append((rad*lengthConversionFactor, scr, alp, bet, gam))
else:
for rad, scr in zip(radii, screen):
gb_List.append((rad*lengthConversionFactor, scr))
return gb_List
def getBoxBetaAndDimensions(self): def getBoxBetaAndDimensions(self):
"""Return periodic boundary box beta angle and dimensions""" """Return periodic boundary box beta angle and dimensions"""
...@@ -502,11 +553,21 @@ class PrmtopLoader(object): ...@@ -502,11 +553,21 @@ class PrmtopLoader(object):
units.Quantity(y, units.angstrom), units.Quantity(y, units.angstrom),
units.Quantity(z, units.angstrom)) units.Quantity(z, units.angstrom))
@property
def has_scee_scnb(self):
return ("SCEE_SCALE_FACTOR" in self._raw_data and "SCNB_SCALE_FACTOR" in self._raw_data)
@property
def has_atomic_number(self):
return 'ATOMIC_NUMBER' in self._raw_data
#============================================================================================= #=============================================================================================
# AMBER System builder (based on, but not identical to, systemManager from 'zander') # AMBER System builder (based on, but not identical to, systemManager from 'zander')
#============================================================================================= #=============================================================================================
def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmodel=None, soluteDielectric=1.0, solventDielectric=78.5, nonbondedCutoff=None, nonbondedMethod='NoCutoff', scee=1.2, scnb=2.0, mm=None, verbose=False, EwaldErrorTolerance=None, flexibleConstraints=True, rigidWater=True): def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmodel=None, soluteDielectric=1.0, solventDielectric=78.5,
nonbondedCutoff=None, nonbondedMethod='NoCutoff', scee=None, scnb=None, mm=None, verbose=False,
EwaldErrorTolerance=None, flexibleConstraints=True, rigidWater=True, elements=None):
""" """
Create an OpenMM System from an Amber prmtop file. Create an OpenMM System from an Amber prmtop file.
...@@ -520,8 +581,8 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode ...@@ -520,8 +581,8 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
soluteDielectric (float) - The solute dielectric constant to use in the implicit solvent model (default: 1.0) soluteDielectric (float) - The solute dielectric constant to use in the implicit solvent model (default: 1.0)
solventDielectric (float) - The solvent dielectric constant to use in the implicit solvent model (default: 78.5) solventDielectric (float) - The solvent dielectric constant to use in the implicit solvent model (default: 78.5)
nonbondedCutoff (float) - if specified, will set nonbondedCutoff (default: None) nonbondedCutoff (float) - if specified, will set nonbondedCutoff (default: None)
scnb (float) - 1-4 Lennard-Jones scaling factor (default: 1.2) scnb (float) - 1-4 Lennard-Jones scaling factor (default: taken from prmtop or 1.2 if not present there)
scee (float) - 1-4 electrostatics scaling factor (default: 2.0) scee (float) - 1-4 electrostatics scaling factor (default: taken from prmtop or 2.0 if not present there)
mm - if specified, this module will be used in place of pyopenmm (default: None) mm - if specified, this module will be used in place of pyopenmm (default: None)
verbose (boolean) - if True, print out information on progress (default: False) verbose (boolean) - if True, print out information on progress (default: False)
flexibleConstraints (boolean) - if True, flexible bonds will be added in addition ot constrained bonds flexibleConstraints (boolean) - if True, flexible bonds will be added in addition ot constrained bonds
...@@ -571,6 +632,10 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode ...@@ -571,6 +632,10 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
if prmtop.getIfBox()>1: if prmtop.getIfBox()>1:
raise Exception("only standard periodic boxes are currently supported") raise Exception("only standard periodic boxes are currently supported")
if prmtop.has_scee_scnb and (scee is not None or scnb is not None):
warnings.warn("1-4 scaling parameters in topology file are being ignored. "
"This is not recommended unless you know what you are doing.")
# Use pyopenmm implementation of OpenMM by default. # Use pyopenmm implementation of OpenMM by default.
if mm is None: if mm is None:
mm = simtk.openmm mm = simtk.openmm
...@@ -615,7 +680,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode ...@@ -615,7 +680,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
if shake == 'h-angles': if shake == 'h-angles':
numConstrainedBonds = system.getNumConstraints() numConstrainedBonds = system.getNumConstraints()
atomConstraints = [[]]*system.getNumParticles() atomConstraints = [[]]*system.getNumParticles()
for i in range(system.getNumConstraints()): for i in range(numConstrainedBonds):
c = system.getConstraintParameters(i) c = system.getConstraintParameters(i)
distance = c[2].value_in_unit(units.nanometer) distance = c[2].value_in_unit(units.nanometer)
atomConstraints[c[0]].append((c[1], distance)) atomConstraints[c[0]].append((c[1], distance))
...@@ -712,9 +777,12 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode ...@@ -712,9 +777,12 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
# Add 1-4 Interactions # Add 1-4 Interactions
excludedAtomPairs = set() excludedAtomPairs = set()
sigmaScale = 2**(-1./6.) sigmaScale = 2**(-1./6.)
for (iAtom, lAtom, chargeProd, rMin, epsilon) in prmtop.get14Interactions(): _scee, _scnb = scee, scnb
chargeProd /= scee for (iAtom, lAtom, chargeProd, rMin, epsilon, iScee, iScnb) in prmtop.get14Interactions():
epsilon /= scnb if scee is None: _scee = iScee
if scnb is None: _scnb = iScnb
chargeProd /= _scee
epsilon /= _scnb
sigma = rMin * sigmaScale sigma = rMin * sigmaScale
force.addException(iAtom, lAtom, chargeProd, sigma, epsilon) force.addException(iAtom, lAtom, chargeProd, sigma, epsilon)
excludedAtomPairs.add(min((iAtom, lAtom), (lAtom, iAtom))) excludedAtomPairs.add(min((iAtom, lAtom), (lAtom, iAtom)))
...@@ -792,25 +860,32 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode ...@@ -792,25 +860,32 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
if gbmodel is not None: if gbmodel is not None:
if verbose: print "Adding GB parameters..." if verbose: print "Adding GB parameters..."
charges = prmtop.getCharges() charges = prmtop.getCharges()
symbls = None cutoff = None
if gbmodel == 'GBn': if nonbondedMethod != 'NoCutoff':
symbls = prmtop.getAtomTypes() cutoff = nonbondedCutoff
gb_parms = prmtop.getGBParms(symbls) if units.is_quantity(cutoff):
cutoff = cutoff.value_in_unit(units.nanometers)
gb_parms = prmtop.getGBParms(gbmodel, elements)
if gbmodel == 'HCT': if gbmodel == 'HCT':
gb = customgb.GBSAHCTForce(solventDielectric, soluteDielectric, 'ACE') gb = customgb.GBSAHCTForce(solventDielectric, soluteDielectric, 'ACE', cutoff)
elif gbmodel == 'OBC1': elif gbmodel == 'OBC1':
gb = customgb.GBSAOBC1Force(solventDielectric, soluteDielectric, 'ACE') gb = customgb.GBSAOBC1Force(solventDielectric, soluteDielectric, 'ACE', cutoff)
elif gbmodel == 'OBC2': elif gbmodel == 'OBC2':
gb = mm.GBSAOBCForce() gb = mm.GBSAOBCForce()
gb.setSoluteDielectric(soluteDielectric) gb.setSoluteDielectric(soluteDielectric)
gb.setSolventDielectric(solventDielectric) gb.setSolventDielectric(solventDielectric)
elif gbmodel == 'GBn': elif gbmodel == 'GBn':
gb = customgb.GBSAGBnForce(solventDielectric, soluteDielectric, 'ACE') gb = customgb.GBSAGBnForce(solventDielectric, soluteDielectric, 'ACE', cutoff)
elif gbmodel == 'GBn2':
gb = customgb.GBSAGBn2Force(solventDielectric, soluteDielectric, 'ACE', cutoff)
else: else:
raise Exception("Illegal value specified for implicit solvent model") raise Exception("Illegal value specified for implicit solvent model")
for iAtom in range(prmtop.getNumAtoms()): for iAtom in range(prmtop.getNumAtoms()):
if gbmodel == 'OBC2': if gbmodel == 'OBC2':
gb.addParticle(charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1]) gb.addParticle(charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1])
elif gbmodel == 'GBn2':
gb.addParticle([charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1],
gb_parms[iAtom][2], gb_parms[iAtom][3], gb_parms[iAtom][4]])
else: else:
gb.addParticle([charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1]]) gb.addParticle([charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1]])
system.addForce(gb) system.addForce(gb)
...@@ -824,6 +899,8 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode ...@@ -824,6 +899,8 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
gb.setCutoffDistance(nonbondedCutoff) gb.setCutoffDistance(nonbondedCutoff)
else: else:
raise Exception("Illegal nonbonded method for use with GBSA") raise Exception("Illegal nonbonded method for use with GBSA")
# This applies the reaction field dielectric to the NonbondedForce
# created above. Do not bind force to another name before this!
force.setReactionFieldDielectric(1.0) force.setReactionFieldDielectric(1.0)
# TODO: Add GBVI terms? # TODO: Add GBVI terms?
......
...@@ -8,7 +8,7 @@ Medical Research, grant U54 GM072970. See https://simtk.org. ...@@ -8,7 +8,7 @@ Medical Research, grant U54 GM072970. See https://simtk.org.
Portions copyright (c) 2012 University of Virginia and the Authors. Portions copyright (c) 2012 University of Virginia and the Authors.
Authors: Christoph Klein, Michael R. Shirts Authors: Christoph Klein, Michael R. Shirts
Contributors: Contributors: Jason M. Swails
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
...@@ -29,8 +29,9 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE ...@@ -29,8 +29,9 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE. USE OR OTHER DEALINGS IN THE SOFTWARE.
""" """
from __future__ import division
from simtk.openmm import CustomGBForce from simtk.openmm import CustomGBForce
import sys, pdb, pickle
d0=[2.26685,2.32548,2.38397,2.44235,2.50057,2.55867,2.61663,2.67444, d0=[2.26685,2.32548,2.38397,2.44235,2.50057,2.55867,2.61663,2.67444,
2.73212,2.78965,2.84705,2.9043,2.96141,3.0184,3.07524,3.13196, 2.73212,2.78965,2.84705,2.9043,2.96141,3.0184,3.07524,3.13196,
...@@ -188,20 +189,35 @@ for i in range (len(d0)): ...@@ -188,20 +189,35 @@ for i in range (len(d0)):
m0[i]=m0[i]*10 m0[i]=m0[i]*10
def _createEnergyTerms(force, SA, cutoff):
# Add the energy terms to the CustomGBForce. These are identical for all the GB models.
force.addEnergyTerm("-0.5*138.935485*(1/soluteDielectric-1/solventDielectric)*q^2/B", CustomGBForce.SingleParticle)
if SA=='ACE':
force.addEnergyTerm("28.3919551*(radius+0.14)^2*(radius/B)^6", CustomGBForce.SingleParticle)
elif SA is not None:
raise ValueError('Unknown surface area method: '+SA)
if cutoff is None:
force.addEnergyTerm("-138.935485*(1/soluteDielectric-1/solventDielectric)*q1*q2/f;"
"f=sqrt(r^2+B1*B2*exp(-r^2/(4*B1*B2)))", CustomGBForce.ParticlePairNoExclusions)
else:
force.addEnergyTerm("-138.935485*(1/soluteDielectric-1/solventDielectric)*q1*q2*(1/f-"+str(1/cutoff)+");"
"f=sqrt(r^2+B1*B2*exp(-r^2/(4*B1*B2)))", CustomGBForce.ParticlePairNoExclusions)
""" """
Amber Equivalent: igb = 1 Amber Equivalent: igb = 1
""" """
def GBSAHCTForce(solventDielectric=78.5, soluteDielectric=1, SA=None): def GBSAHCTForce(solventDielectric=78.5, soluteDielectric=1, SA=None, cutoff=None):
custom = CustomGBForce() custom = CustomGBForce()
custom.addPerParticleParameter("q"); custom.addPerParticleParameter("q")
custom.addPerParticleParameter("radius"); custom.addPerParticleParameter("radius")
custom.addPerParticleParameter("scale"); custom.addPerParticleParameter("scale")
custom.addGlobalParameter("solventDielectric", solventDielectric); custom.addGlobalParameter("solventDielectric", solventDielectric)
custom.addGlobalParameter("soluteDielectric", soluteDielectric); custom.addGlobalParameter("soluteDielectric", soluteDielectric)
custom.addGlobalParameter("offset", 0.009) custom.addGlobalParameter("offset", 0.009)
custom.addComputedValue("I", "step(r+sr2-or1)*0.5*(1/L-1/U+0.25*(r-sr2^2/r)*(1/(U^2)-1/(L^2))+0.5*log(L/U)/r);" custom.addComputedValue("I", "step(r+sr2-or1)*0.5*(1/L-1/U+0.25*(r-sr2^2/r)*(1/(U^2)-1/(L^2))+0.5*log(L/U)/r);"
"U=r+sr2;" "U=r+sr2;"
...@@ -212,30 +228,21 @@ def GBSAHCTForce(solventDielectric=78.5, soluteDielectric=1, SA=None): ...@@ -212,30 +228,21 @@ def GBSAHCTForce(solventDielectric=78.5, soluteDielectric=1, SA=None):
custom.addComputedValue("B", "1/(1/or-I);" custom.addComputedValue("B", "1/(1/or-I);"
"or=radius-offset", CustomGBForce.SingleParticle) "or=radius-offset", CustomGBForce.SingleParticle)
_createEnergyTerms(custom, SA, cutoff)
custom.addEnergyTerm("-0.5*138.935485*(1/soluteDielectric-1/solventDielectric)*q^2/B", CustomGBForce.SingleParticle)
if SA=='ACE':
custom.addEnergyTerm("28.3919551*(radius+0.14)^2*(radius/B)^6", CustomGBForce.SingleParticle)
elif SA is not None:
raise ValueError('Unknown surface area method: '+SA)
custom.addEnergyTerm("-138.935485*(1/soluteDielectric-1/solventDielectric)*q1*q2/f;"
"f=sqrt(r^2+B1*B2*exp(-r^2/(4*B1*B2)))", CustomGBForce.ParticlePairNoExclusions)
return custom return custom
""" """
Amber Equivalents: igb = 2 Amber Equivalents: igb = 2
""" """
def GBSAOBC1Force(solventDielectric=78.5, soluteDielectric=1, SA=None): def GBSAOBC1Force(solventDielectric=78.5, soluteDielectric=1, SA=None, cutoff=None):
custom = CustomGBForce() custom = CustomGBForce()
custom.addPerParticleParameter("q"); custom.addPerParticleParameter("q")
custom.addPerParticleParameter("radius"); custom.addPerParticleParameter("radius")
custom.addPerParticleParameter("scale"); custom.addPerParticleParameter("scale")
custom.addGlobalParameter("solventDielectric", solventDielectric); custom.addGlobalParameter("solventDielectric", solventDielectric)
custom.addGlobalParameter("soluteDielectric", soluteDielectric); custom.addGlobalParameter("soluteDielectric", soluteDielectric)
custom.addGlobalParameter("offset", 0.009) custom.addGlobalParameter("offset", 0.009)
custom.addComputedValue("I", "step(r+sr2-or1)*0.5*(1/L-1/U+0.25*(r-sr2^2/r)*(1/(U^2)-1/(L^2))+0.5*log(L/U)/r);" custom.addComputedValue("I", "step(r+sr2-or1)*0.5*(1/L-1/U+0.25*(r-sr2^2/r)*(1/(U^2)-1/(L^2))+0.5*log(L/U)/r);"
"U=r+sr2;" "U=r+sr2;"
...@@ -246,29 +253,21 @@ def GBSAOBC1Force(solventDielectric=78.5, soluteDielectric=1, SA=None): ...@@ -246,29 +253,21 @@ def GBSAOBC1Force(solventDielectric=78.5, soluteDielectric=1, SA=None):
custom.addComputedValue("B", "1/(1/or-tanh(0.8*psi+2.909125*psi^3)/radius);" custom.addComputedValue("B", "1/(1/or-tanh(0.8*psi+2.909125*psi^3)/radius);"
"psi=I*or; or=radius-offset", CustomGBForce.SingleParticle) "psi=I*or; or=radius-offset", CustomGBForce.SingleParticle)
_createEnergyTerms(custom, SA, cutoff)
custom.addEnergyTerm("-0.5*138.935485*(1/soluteDielectric-1/solventDielectric)*q^2/B", CustomGBForce.SingleParticle)
if SA=='ACE':
custom.addEnergyTerm("28.3919551*(radius+0.14)^2*(radius/B)^6", CustomGBForce.SingleParticle)
elif SA is not None:
raise ValueError('Unknown surface area method: '+SA)
custom.addEnergyTerm("-138.935485*(1/soluteDielectric-1/solventDielectric)*q1*q2/f;"
"f=sqrt(r^2+B1*B2*exp(-r^2/(4*B1*B2)))", CustomGBForce.ParticlePairNoExclusions)
return custom return custom
""" """
Amber Equivalents: igb = 5 Amber Equivalents: igb = 5
""" """
def GBSAOBC2Force(solventDielectric=78.5, soluteDielectric=1, SA=None): def GBSAOBC2Force(solventDielectric=78.5, soluteDielectric=1, SA=None, cutoff=None):
custom = CustomGBForce() custom = CustomGBForce()
custom.addPerParticleParameter("q"); custom.addPerParticleParameter("q")
custom.addPerParticleParameter("radius"); custom.addPerParticleParameter("radius")
custom.addPerParticleParameter("scale"); custom.addPerParticleParameter("scale")
custom.addGlobalParameter("solventDielectric", solventDielectric); custom.addGlobalParameter("solventDielectric", solventDielectric)
custom.addGlobalParameter("soluteDielectric", soluteDielectric); custom.addGlobalParameter("soluteDielectric", soluteDielectric)
custom.addGlobalParameter("offset", 0.009) custom.addGlobalParameter("offset", 0.009)
custom.addComputedValue("I", "step(r+sr2-or1)*0.5*(1/L-1/U+0.25*(r-sr2^2/r)*(1/(U^2)-1/(L^2))+0.5*log(L/U)/r);" custom.addComputedValue("I", "step(r+sr2-or1)*0.5*(1/L-1/U+0.25*(r-sr2^2/r)*(1/(U^2)-1/(L^2))+0.5*log(L/U)/r);"
"U=r+sr2;" "U=r+sr2;"
...@@ -279,21 +278,13 @@ def GBSAOBC2Force(solventDielectric=78.5, soluteDielectric=1, SA=None): ...@@ -279,21 +278,13 @@ def GBSAOBC2Force(solventDielectric=78.5, soluteDielectric=1, SA=None):
custom.addComputedValue("B", "1/(1/or-tanh(psi-0.8*psi^2+4.85*psi^3)/radius);" custom.addComputedValue("B", "1/(1/or-tanh(psi-0.8*psi^2+4.85*psi^3)/radius);"
"psi=I*or; or=radius-offset", CustomGBForce.SingleParticle) "psi=I*or; or=radius-offset", CustomGBForce.SingleParticle)
_createEnergyTerms(custom, SA, cutoff)
custom.addEnergyTerm("-0.5*138.935485*(1/soluteDielectric-1/solventDielectric)*q^2/B", CustomGBForce.SingleParticle)
if SA=='ACE':
custom.addEnergyTerm("28.3919551*(radius+0.14)^2*(radius/B)^6", CustomGBForce.SingleParticle)
elif SA is not None:
raise ValueError('Unknown surface area method: '+SA)
custom.addEnergyTerm("-138.935485*(1/soluteDielectric-1/solventDielectric)*q1*q2/f;"
"f=sqrt(r^2+B1*B2*exp(-r^2/(4*B1*B2)))", CustomGBForce.ParticlePairNoExclusions)
return custom return custom
""" """
Amber Equivalents: igb = 7 Amber Equivalents: igb = 7
""" """
def GBSAGBnForce(solventDielectric=78.5, soluteDielectric=1, SA=None): def GBSAGBnForce(solventDielectric=78.5, soluteDielectric=1, SA=None, cutoff=None):
""" """
...@@ -306,12 +297,12 @@ def GBSAGBnForce(solventDielectric=78.5, soluteDielectric=1, SA=None): ...@@ -306,12 +297,12 @@ def GBSAGBnForce(solventDielectric=78.5, soluteDielectric=1, SA=None):
custom = CustomGBForce() custom = CustomGBForce()
custom.addPerParticleParameter("q"); custom.addPerParticleParameter("q")
custom.addPerParticleParameter("radius"); custom.addPerParticleParameter("radius")
custom.addPerParticleParameter("scale"); custom.addPerParticleParameter("scale")
custom.addGlobalParameter("solventDielectric", solventDielectric); custom.addGlobalParameter("solventDielectric", solventDielectric)
custom.addGlobalParameter("soluteDielectric", soluteDielectric); custom.addGlobalParameter("soluteDielectric", soluteDielectric)
custom.addGlobalParameter("offset", 0.009) custom.addGlobalParameter("offset", 0.009)
custom.addGlobalParameter("neckScale", 0.361825) custom.addGlobalParameter("neckScale", 0.361825)
custom.addGlobalParameter("neckCut", 0.68) custom.addGlobalParameter("neckCut", 0.68)
...@@ -331,13 +322,52 @@ def GBSAGBnForce(solventDielectric=78.5, soluteDielectric=1, SA=None): ...@@ -331,13 +322,52 @@ def GBSAGBnForce(solventDielectric=78.5, soluteDielectric=1, SA=None):
custom.addComputedValue("B", "1/(1/or-tanh(1.09511284*psi-1.907992938*psi^2+2.50798245*psi^3)/radius);" custom.addComputedValue("B", "1/(1/or-tanh(1.09511284*psi-1.907992938*psi^2+2.50798245*psi^3)/radius);"
"psi=I*or; or=radius-offset", CustomGBForce.SingleParticle) "psi=I*or; or=radius-offset", CustomGBForce.SingleParticle)
_createEnergyTerms(custom, SA, cutoff)
return custom
custom.addEnergyTerm("-0.5*138.935485*(1/soluteDielectric-1/solventDielectric)*q^2/B", CustomGBForce.SingleParticle) """
if SA=='ACE': Amber Equivalents: igb = 8
custom.addEnergyTerm("28.3919551*(radius+0.14)^2*(radius/B)^6", CustomGBForce.SingleParticle) """
elif SA is not None: def GBSAGBn2Force(solventDielectric=78.5, soluteDielectric=1, SA=None, cutoff=None):
raise ValueError('Unknown surface area method: '+SA)
custom.addEnergyTerm("-138.935485*(1/soluteDielectric-1/solventDielectric)*q1*q2/f;"
"f=sqrt(r^2+B1*B2*exp(-r^2/(4*B1*B2)))", CustomGBForce.ParticlePairNoExclusions)
"""
Indexing for tables:
input: radius1, radius2
index = (radius2*200-20)*21 + (radius1*200-20)
output: index of desired value in row-by-row, 1D version of Tables 3 & 4
"""
custom = CustomGBForce()
custom.addPerParticleParameter("q")
custom.addPerParticleParameter("radius")
custom.addPerParticleParameter("scale")
custom.addPerParticleParameter("alpha")
custom.addPerParticleParameter("beta")
custom.addPerParticleParameter("gamma")
custom.addGlobalParameter("solventDielectric", solventDielectric)
custom.addGlobalParameter("soluteDielectric", soluteDielectric)
custom.addGlobalParameter("offset", 0.0195141)
custom.addGlobalParameter("neckScale", 0.826836)
custom.addGlobalParameter("neckCut", 0.68)
custom.addFunction("getd0", d0, 0, 440)
custom.addFunction("getm0", m0, 0, 440)
custom.addComputedValue("I", "Ivdw+neckScale*Ineck;"
"Ineck=step(radius1+radius2+neckCut-r)*getm0(index)/(1+100*(r-getd0(index))^2+0.3*1000000*(r-getd0(index))^6);"
"index = (radius2*200-20)*21 + (radius1*200-20);"
"Ivdw=step(r+sr2-or1)*0.5*(1/L-1/U+0.25*(r-sr2^2/r)*(1/(U^2)-1/(L^2))+0.5*log(L/U)/r);"
"U=r+sr2;"
"L=max(or1, D);"
"D=abs(r-sr2);"
"sr2 = scale2*or2;"
"or1 = radius1-offset; or2 = radius2-offset", CustomGBForce.ParticlePairNoExclusions)
custom.addComputedValue("B", "1/(1/or-tanh(alpha*psi-beta*psi^2+gamma*psi^3)/radius);"
"psi=I*or; or=radius-offset", CustomGBForce.SingleParticle)
_createEnergyTerms(custom, SA, cutoff)
return custom return custom
...@@ -8,7 +8,7 @@ Simbios, the NIH National Center for Physics-Based Simulation of ...@@ -8,7 +8,7 @@ Simbios, the NIH National Center for Physics-Based Simulation of
Biological Structures at Stanford, funded under the NIH Roadmap for Biological Structures at Stanford, funded under the NIH Roadmap for
Medical Research, grant U54 GM072970. See https://simtk.org. Medical Research, grant U54 GM072970. See https://simtk.org.
Portions copyright (c) 2012 Stanford University and the Authors. Portions copyright (c) 2012-2013 Stanford University and the Authors.
Authors: Christopher M. Bruns Authors: Christopher M. Bruns
Contributors: Peter Eastman Contributors: Peter Eastman
...@@ -138,6 +138,8 @@ class PdbStructure(object): ...@@ -138,6 +138,8 @@ class PdbStructure(object):
self.default_model = None self.default_model = None
self.models_by_number = {} self.models_by_number = {}
self._unit_cell_dimensions = None self._unit_cell_dimensions = None
self.sequences = []
self.modified_residues = []
# read file # read file
self._load(input_stream) self._load(input_stream)
...@@ -172,6 +174,13 @@ class PdbStructure(object): ...@@ -172,6 +174,13 @@ class PdbStructure(object):
except: except:
pass pass
self._current_model.connects.append(atoms) self._current_model.connects.append(atoms)
elif (pdb_line.find("SEQRES") == 0):
chain_id = pdb_line[11]
if len(self.sequences) == 0 or chain_id != self.sequences[-1].chain_id:
self.sequences.append(Sequence(chain_id))
self.sequences[-1].residues.extend(pdb_line[19:].split())
elif (pdb_line.find("MODRES") == 0):
self.modified_residues.append(ModifiedResidue(pdb_line[16], int(pdb_line[18:22]), pdb_line[12:15].strip(), pdb_line[24:27].strip()))
self._finalize() self._finalize()
def write(self, output_stream=sys.stdout): def write(self, output_stream=sys.stdout):
...@@ -266,6 +275,21 @@ class PdbStructure(object): ...@@ -266,6 +275,21 @@ class PdbStructure(object):
return self._unit_cell_dimensions return self._unit_cell_dimensions
class Sequence(object):
"""Sequence holds the sequence of a chain, as specified by SEQRES records."""
def __init__(self, chain_id):
self.chain_id = chain_id
self.residues = []
class ModifiedResidue(object):
"""ModifiedResidue holds information about a modified residue, as specified by a MODRES record."""
def __init__(self, chain_id, number, residue_name, standard_name):
self.chain_id = chain_id
self.number = number
self.residue_name = residue_name
self.standard_name = standard_name
class Model(object): class Model(object):
"""Model holds one model of a PDB structure. """Model holds one model of a PDB structure.
...@@ -412,7 +436,7 @@ class Chain(object): ...@@ -412,7 +436,7 @@ class Chain(object):
self._finalize() self._finalize()
def get_residue(self, residue_number, insertion_code=' '): def get_residue(self, residue_number, insertion_code=' '):
return residues_by_num_icode[str(residue_number) + insertion_code] return self.residues_by_num_icode[str(residue_number) + insertion_code]
def __contains__(self, residue_number): def __contains__(self, residue_number):
return self.residues_by_number.__contains__(residue_number) return self.residues_by_number.__contains__(residue_number)
...@@ -892,7 +916,6 @@ if __name__=='__main__': ...@@ -892,7 +916,6 @@ if __name__=='__main__':
import doctest, sys import doctest, sys
doctest.testmod(sys.modules[__name__]) doctest.testmod(sys.modules[__name__])
import sys
import os import os
import gzip import gzip
import re import re
......
...@@ -37,7 +37,7 @@ from simtk.openmm.app import Topology, PDBFile, ForceField ...@@ -37,7 +37,7 @@ from simtk.openmm.app import Topology, PDBFile, ForceField
from simtk.openmm.app.forcefield import HAngles, _createResidueSignature, _matchResidue, DrudeGenerator from simtk.openmm.app.forcefield import HAngles, _createResidueSignature, _matchResidue, DrudeGenerator
from simtk.openmm.app.topology import Residue from simtk.openmm.app.topology import Residue
from simtk.openmm.vec3 import Vec3 from simtk.openmm.vec3 import Vec3
from simtk.openmm import System, Context, NonbondedForce, VerletIntegrator, LocalEnergyMinimizer from simtk.openmm import System, Context, NonbondedForce, CustomNonbondedForce, HarmonicBondForce, HarmonicAngleForce, VerletIntegrator, LocalEnergyMinimizer
from simtk.unit import nanometer, molar, elementary_charge, amu, gram, liter, degree, sqrt, acos, is_quantity, dot, norm from simtk.unit import nanometer, molar, elementary_charge, amu, gram, liter, degree, sqrt, acos, is_quantity, dot, norm
import element as elem import element as elem
import os import os
...@@ -519,7 +519,7 @@ class Modeller(object): ...@@ -519,7 +519,7 @@ class Modeller(object):
terminal = hydrogen.attrib['terminal'] terminal = hydrogen.attrib['terminal']
data.hydrogens.append(Modeller._Hydrogen(hydrogen.attrib['name'], hydrogen.attrib['parent'], maxph, atomVariants, terminal)) data.hydrogens.append(Modeller._Hydrogen(hydrogen.attrib['name'], hydrogen.attrib['parent'], maxph, atomVariants, terminal))
def addHydrogens(self, forcefield, pH=7.0, variants=None, platform=None): def addHydrogens(self, forcefield=None, pH=7.0, variants=None, platform=None):
"""Add missing hydrogens to the model. """Add missing hydrogens to the model.
Some residues can exist in multiple forms depending on the pH and properties of the local environment. These Some residues can exist in multiple forms depending on the pH and properties of the local environment. These
...@@ -561,7 +561,8 @@ class Modeller(object): ...@@ -561,7 +561,8 @@ class Modeller(object):
additional definitions for other residue types. additional definitions for other residue types.
Parameters: Parameters:
- forcefield (ForceField) the ForceField to use for determining the positions of hydrogens - forcefield (ForceField=None) the ForceField to use for determining the positions of hydrogens. If this is None,
positions will be picked which are generally reasonable but not optimized for any particular ForceField.
- pH (float=7.0) the pH based on which to select variants - pH (float=7.0) the pH based on which to select variants
- variants (list=None) an optional list of variants to use. If this is specified, its length must equal the number - variants (list=None) an optional list of variants to use. If this is specified, its length must equal the number
of residues in the model. variants[i] is the name of the variant to use for residue i (indexed starting at 0). of residues in the model. variants[i] is the name of the variant to use for residue i (indexed starting at 0).
...@@ -753,14 +754,45 @@ class Modeller(object): ...@@ -753,14 +754,45 @@ class Modeller(object):
if bond[0] in newAtoms and bond[1] in newAtoms: if bond[0] in newAtoms and bond[1] in newAtoms:
newTopology.addBond(newAtoms[bond[0]], newAtoms[bond[1]]) newTopology.addBond(newAtoms[bond[0]], newAtoms[bond[1]])
# The hydrogens were added at random positions. Now use the ForceField to fix them up. # The hydrogens were added at random positions. Now perform an energy minimization to fix them up.
system = forcefield.createSystem(newTopology, rigidWater=False) if forcefield is not None:
atoms = list(newTopology.atoms()) # Use the ForceField the user specified.
for i in range(system.getNumParticles()):
if atoms[i].element != elem.hydrogen: system = forcefield.createSystem(newTopology, rigidWater=False)
# This is a heavy atom, so make it immobile. atoms = list(newTopology.atoms())
system.setParticleMass(i, 0) for i in range(system.getNumParticles()):
if atoms[i].element != elem.hydrogen:
# This is a heavy atom, so make it immobile.
system.setParticleMass(i, 0)
else:
# Create a System that restrains the distance of each hydrogen from its parent atom
# and causes hydrogens to spread out evenly.
system = System()
nonbonded = CustomNonbondedForce('1/((r/0.1)^4+1)')
bonds = HarmonicBondForce()
angles = HarmonicAngleForce()
system.addForce(nonbonded)
system.addForce(bonds)
system.addForce(angles)
for atom in newTopology.atoms():
nonbonded.addParticle([])
if atom.element != elem.hydrogen:
system.addParticle(0.0)
else:
system.addParticle(1.0)
for atom1, atom2 in newTopology.bonds():
if atom1.element == elem.hydrogen or atom2.element == elem.hydrogen:
bonds.addBond(atom1.index, atom2.index, 0.1, 100000.0)
for residue in newTopology.residues():
if residue.name == 'HOH':
atoms = list(residue.atoms())
oindex = [i for i in range(len(atoms)) if atoms[i].element == elem.oxygen]
if len(atoms) == 3 and len(oindex) == 1:
hindex = list(set([0,1,2])-set(oindex))
angles.addAngle(atoms[hindex[0]].index, atoms[oindex[0]].index, atoms[hindex[1]].index, 1.824, 836.8)
if platform is None: if platform is None:
context = Context(system, VerletIntegrator(0.0)) context = Context(system, VerletIntegrator(0.0))
else: else:
......
...@@ -36,7 +36,8 @@ import sys ...@@ -36,7 +36,8 @@ import sys
import math import math
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
from copy import copy from copy import copy
from simtk.openmm import Vec3 from datetime import date
from simtk.openmm import Vec3, Platform
from simtk.openmm.app.internal.pdbstructure import PdbStructure from simtk.openmm.app.internal.pdbstructure import PdbStructure
from simtk.openmm.app import Topology from simtk.openmm.app import Topology
from simtk.unit import nanometers, angstroms, is_quantity, norm, Quantity from simtk.unit import nanometers, angstroms, is_quantity, norm, Quantity
...@@ -70,7 +71,13 @@ class PDBFile(object): ...@@ -70,7 +71,13 @@ class PDBFile(object):
# Load the PDB file # Load the PDB file
pdb = PdbStructure(open(file), load_all_models=True) if isinstance(file, PdbStructure):
pdb = file
else:
inputfile = file
if isinstance(file, str):
inputfile = open(file)
pdb = PdbStructure(inputfile, load_all_models=True)
PDBFile._loadNameReplacementTables() PDBFile._loadNameReplacementTables()
# Build the topology # Build the topology
...@@ -140,7 +147,8 @@ class PDBFile(object): ...@@ -140,7 +147,8 @@ class PDBFile(object):
for connect in pdb.models[0].connects: for connect in pdb.models[0].connects:
i = connect[0] i = connect[0]
for j in connect[1:]: for j in connect[1:]:
connectBonds.append((atomByNumber[i], atomByNumber[j])) if i in atomByNumber and j in atomByNumber:
connectBonds.append((atomByNumber[i], atomByNumber[j]))
if len(connectBonds) > 0: if len(connectBonds) > 0:
# Only add bonds that don't already exist. # Only add bonds that don't already exist.
existingBonds = set(top.bonds()) existingBonds = set(top.bonds())
...@@ -235,6 +243,7 @@ class PDBFile(object): ...@@ -235,6 +243,7 @@ class PDBFile(object):
- topology (Topology) The Topology defining the molecular system being written - topology (Topology) The Topology defining the molecular system being written
- file (file=stdout) A file to write the file to - file (file=stdout) A file to write the file to
""" """
print >>file, "REMARK 1 CREATED WITH OPENMM %s, %s" % (Platform.getOpenMMVersion(), str(date.today()))
boxSize = topology.getUnitCellDimensions() boxSize = topology.getUnitCellDimensions()
if boxSize is not None: if boxSize is not None:
size = boxSize.value_in_unit(angstroms) size = boxSize.value_in_unit(angstroms)
...@@ -278,11 +287,15 @@ class PDBFile(object): ...@@ -278,11 +287,15 @@ class PDBFile(object):
else: else:
atomName = atom.name atomName = atom.name
coords = positions[posIndex] coords = positions[posIndex]
line = "ATOM %5d %-4s %3s %s%4d %s%s%s 1.00 0.00" % ( if atom.element is not None:
symbol = atom.element.symbol
else:
symbol = ' '
line = "ATOM %5d %-4s %3s %s%4d %s%s%s 1.00 0.00 %2s " % (
atomIndex%100000, atomName, resName, chainName, atomIndex%100000, atomName, resName, chainName,
(resIndex+1)%10000, _format_83(coords[0]), (resIndex+1)%10000, _format_83(coords[0]),
_format_83(coords[1]), _format_83(coords[2])) _format_83(coords[1]), _format_83(coords[2]), symbol)
assert len(line) == 66, 'Fixed width overflow detected' assert len(line) == 80, 'Fixed width overflow detected'
print >>file, line print >>file, line
posIndex += 1 posIndex += 1
atomIndex += 1 atomIndex += 1
...@@ -300,6 +313,56 @@ class PDBFile(object): ...@@ -300,6 +313,56 @@ class PDBFile(object):
- topology (Topology) The Topology defining the molecular system being written - topology (Topology) The Topology defining the molecular system being written
- file (file=stdout) A file to write the file to - file (file=stdout) A file to write the file to
""" """
# Identify bonds that should be listed as CONECT records.
standardResidues = ['ALA', 'ASN', 'CYS', 'GLU', 'HIS', 'LEU', 'MET', 'PRO', 'THR', 'TYR',
'ARG', 'ASP', 'GLN', 'GLY', 'ILE', 'LYS', 'PHE', 'SER', 'TRP', 'VAL',
'A', 'G', 'C', 'U', 'I', 'DA', 'DG', 'DC', 'DT', 'DI', 'HOH']
conectBonds = []
for atom1, atom2 in topology.bonds():
if atom1.residue.name not in standardResidues or atom2.residue.name not in standardResidues:
conectBonds.append((atom1, atom2))
elif atom1.name == 'SG' and atom2.name == 'SG' and atom1.residue.name == 'CYS' and atom2.residue.name == 'CYS':
conectBonds.append((atom1, atom2))
if len(conectBonds) > 0:
# Work out the index used in the PDB file for each atom.
atomIndex = {}
nextAtomIndex = 0
prevChain = None
for chain in topology.chains():
for atom in chain.atoms():
if atom.residue.chain != prevChain:
nextAtomIndex += 1
prevChain = atom.residue.chain
atomIndex[atom] = nextAtomIndex
nextAtomIndex += 1
# Record which other atoms each atom is bonded to.
atomBonds = {}
for atom1, atom2 in conectBonds:
index1 = atomIndex[atom1]
index2 = atomIndex[atom2]
if index1 not in atomBonds:
atomBonds[index1] = []
if index2 not in atomBonds:
atomBonds[index2] = []
atomBonds[index1].append(index2)
atomBonds[index2].append(index1)
# Write the CONECT records.
for index1 in sorted(atomBonds):
bonded = atomBonds[index1]
while len(bonded) > 4:
print >>file, "CONECT%5d%5d%5d%5d" % (index1, bonded[0], bonded[1], bonded[2])
del bonded[:4]
line = "CONECT%5d" % index1
for index2 in bonded:
line = "%s%5d" % (line, index2)
print >>file, line
print >>file, "END" print >>file, "END"
......
...@@ -79,5 +79,6 @@ class PDBReporter(object): ...@@ -79,5 +79,6 @@ class PDBReporter(object):
self._nextModel += 1 self._nextModel += 1
def __del__(self): def __del__(self):
PDBFile.writeFooter(self._topology, self._out) if self._topology is not None:
PDBFile.writeFooter(self._topology, self._out)
self._out.close() self._out.close()
...@@ -31,9 +31,10 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -31,9 +31,10 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
__author__ = "Peter Eastman" __author__ = "Peter Eastman"
__version__ = "1.0" __version__ = "1.0"
import bz2
import gzip
import simtk.openmm as mm import simtk.openmm as mm
import simtk.unit as unit import simtk.unit as unit
from simtk.openmm.app import PDBFile
import math import math
class StateDataReporter(object): class StateDataReporter(object):
...@@ -67,7 +68,14 @@ class StateDataReporter(object): ...@@ -67,7 +68,14 @@ class StateDataReporter(object):
self._reportInterval = reportInterval self._reportInterval = reportInterval
self._openedFile = isinstance(file, str) self._openedFile = isinstance(file, str)
if self._openedFile: if self._openedFile:
self._out = open(file, 'w', 0) # Detect the desired compression scheme from the filename extension
# and open all files unbuffered
if file.endswith('.gz'):
self._out = gzip.GzipFile(fileobj=open(file, 'wb', 0))
elif file.endswith('.bz2'):
self._out = bz2.BZ2File(file, 'w', 0)
else:
self._out = open(file, 'w', 0)
else: else:
self._out = file self._out = file
self._step = step self._step = step
...@@ -109,7 +117,10 @@ class StateDataReporter(object): ...@@ -109,7 +117,10 @@ class StateDataReporter(object):
self._initializeConstants(simulation) self._initializeConstants(simulation)
headers = self._constructHeaders() headers = self._constructHeaders()
print >>self._out, '#"%s"' % ('"'+self._separator+'"').join(headers) print >>self._out, '#"%s"' % ('"'+self._separator+'"').join(headers)
self._out.flush() try:
self._out.flush()
except AttributeError:
pass
self._hasInitialized = True self._hasInitialized = True
# Check for errors. # Check for errors.
...@@ -120,7 +131,10 @@ class StateDataReporter(object): ...@@ -120,7 +131,10 @@ class StateDataReporter(object):
# Write the values. # Write the values.
print >>self._out, self._separator.join(str(v) for v in values) print >>self._out, self._separator.join(str(v) for v in values)
self._out.flush() try:
self._out.flush()
except AttributeError:
pass
def _constructReportValues(self, simulation, state): def _constructReportValues(self, simulation, state):
"""Query the simulation for the current state of our observables of interest. """Query the simulation for the current state of our observables of interest.
......
...@@ -616,6 +616,7 @@ EXCLUDE_SYMLINKS = NO ...@@ -616,6 +616,7 @@ EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS = */tests/* \ EXCLUDE_PATTERNS = */tests/* \
*/openmmapi/src/* \ */openmmapi/src/* \
*/internal/* \
*/.svn/* \ */.svn/* \
*OpenMMFortranModule.f90 \ *OpenMMFortranModule.f90 \
*OpenMMCWrapper.h *OpenMMCWrapper.h
......
#!/bin/env python #!/usr/bin/env python
# #
# #
......
...@@ -14,20 +14,7 @@ DOC_STRINGS = {("Context", "setPositions") : ...@@ -14,20 +14,7 @@ DOC_STRINGS = {("Context", "setPositions") :
# Do not generate wrappers for the following methods. # Do not generate wrappers for the following methods.
# Indexed by (className, [methodName [, numParams]]) # Indexed by (className, [methodName [, numParams]])
SKIP_METHODS = [('State',), SKIP_METHODS = [('State',),
('Stream',),
('Vec3',), ('Vec3',),
('AmoebaGeneralizedKirkwoodForceImpl',),
('AmoebaAngleForceImpl',),
('AmoebaBondForceImpl',),
('AmoebaInPlaneAngleForceImpl',),
('AmoebaMultipoleForceImpl',),
('AmoebaOutOfPlaneBendForceImpl',),
('AmoebaPiTorsionForceImpl',),
('AmoebaStretchBendForceImpl',),
('AmoebaTorsionTorsionForceImpl',),
('AmoebaVdwForceImpl',),
('AmoebaWcaDispersionForceImpl',),
('AndersenThermostatImpl',),
('AngleInfo',), ('AngleInfo',),
('ApplyAndersenThermostatKernel',), ('ApplyAndersenThermostatKernel',),
('ApplyConstraintsKernel',), ('ApplyConstraintsKernel',),
...@@ -63,30 +50,14 @@ SKIP_METHODS = [('State',), ...@@ -63,30 +50,14 @@ SKIP_METHODS = [('State',),
('CalcNonbondedForceKernel',), ('CalcNonbondedForceKernel',),
('CalcPeriodicTorsionForceKernel',), ('CalcPeriodicTorsionForceKernel',),
('CalcRBTorsionForceKernel',), ('CalcRBTorsionForceKernel',),
('CMAPTorsionForceImpl',),
('CMMotionRemoverImpl',),
('ComputationInfo',), ('ComputationInfo',),
('ConstraintInfo',), ('ConstraintInfo',),
('ContextImpl',),
('CudaKernelFactory',), ('CudaKernelFactory',),
('CudaStreamFactory',), ('CudaStreamFactory',),
('CustomAngleForceImpl',),
('CustomBondForceImpl',),
('CustomCompoundBondForceImpl',),
('CustomExternalForceImpl',),
('CustomGBForceImpl',),
('CustomHbondForceImpl',),
('CustomNonbondedForceImpl',),
('CustomTorsionForceImpl',),
('ExceptionInfo',), ('ExceptionInfo',),
('ExclusionInfo',), ('ExclusionInfo',),
('ForceImpl',),
('FunctionInfo',), ('FunctionInfo',),
('GBSAOBCForceImpl',),
('GBVIForceImpl',),
('GlobalParameterInfo',), ('GlobalParameterInfo',),
('HarmonicAngleForceImpl',),
('HarmonicBondForceImpl',),
('InitializeForcesKernel',), ('InitializeForcesKernel',),
('IntegrateBrownianStepKernel',), ('IntegrateBrownianStepKernel',),
('IntegrateLangevinStepKernel',), ('IntegrateLangevinStepKernel',),
...@@ -97,24 +68,18 @@ SKIP_METHODS = [('State',), ...@@ -97,24 +68,18 @@ SKIP_METHODS = [('State',),
('Kernel',), ('Kernel',),
('KernelFactory',), ('KernelFactory',),
('KernelImpl',), ('KernelImpl',),
('MonteCarloBarostatImpl',),
('MonteCarloAnisotropicBarostatImpl',),
('MultipoleInfo',), ('MultipoleInfo',),
('NonbondedForceImpl',),
('OutOfPlaneBendInfo',), ('OutOfPlaneBendInfo',),
('ParameterInfo',), ('ParameterInfo',),
('ParticleInfo',), ('ParticleInfo',),
('PeriodicTorsionForceImpl',),
('PeriodicTorsionInfo',), ('PeriodicTorsionInfo',),
('PerParticleParameterInfo',), ('PerParticleParameterInfo',),
('PiTorsionInfo',), ('PiTorsionInfo',),
('PlatformData',), ('PlatformData',),
('RBTorsionForceImpl',),
('RBTorsionInfo',), ('RBTorsionInfo',),
('RemoveCMMotionKernel',), ('RemoveCMMotionKernel',),
('SplineFitter',), ('SplineFitter',),
('StreamFactory',), ('StreamFactory',),
('StreamImpl',),
('StretchBendInfo',), ('StretchBendInfo',),
('TorsionInfo',), ('TorsionInfo',),
('TorsionTorsionGridInfo',), ('TorsionTorsionGridInfo',),
...@@ -139,7 +104,6 @@ SKIP_METHODS = [('State',), ...@@ -139,7 +104,6 @@ SKIP_METHODS = [('State',),
('Platform', 'registerKernelFactory'), ('Platform', 'registerKernelFactory'),
('IntegrateRPMDStepKernel',), ('IntegrateRPMDStepKernel',),
('RPMDIntegrator', 'getState'), ('RPMDIntegrator', 'getState'),
('DrudeForceImpl',),
('CalcDrudeForceKernel',), ('CalcDrudeForceKernel',),
('IntegrateDrudeLangevinStepKernel',), ('IntegrateDrudeLangevinStepKernel',),
('IntegrateDrudeSCFStepKernel',), ('IntegrateDrudeSCFStepKernel',),
...@@ -159,6 +123,8 @@ NO_OUTPUT_ARGS = [('LocalEnergyMinimizer', 'minimize', 'context'), ...@@ -159,6 +123,8 @@ NO_OUTPUT_ARGS = [('LocalEnergyMinimizer', 'minimize', 'context'),
('AmoebaMultipoleForce', 'addParticle', 'molecularDipole'), ('AmoebaMultipoleForce', 'addParticle', 'molecularDipole'),
('AmoebaMultipoleForce', 'addParticle', 'molecularQuadrupole'), ('AmoebaMultipoleForce', 'addParticle', 'molecularQuadrupole'),
('AmoebaMultipoleForce', 'setCovalentMap', 'covalentAtoms'), ('AmoebaMultipoleForce', 'setCovalentMap', 'covalentAtoms'),
('AmoebaMultipoleForce', 'getElectrostaticPotential', 'context'),
('AmoebaMultipoleForce', 'getInducedDipoles', 'context'),
] ]
# SWIG assumes the target language shadow class owns the C++ class # SWIG assumes the target language shadow class owns the C++ class
...@@ -283,6 +249,7 @@ UNITS = { ...@@ -283,6 +249,7 @@ UNITS = {
#("AmoebaMultipoleForce", "getElectrostaticPotential") : ( None, ('unit.kilojoule_per_mole')), #("AmoebaMultipoleForce", "getElectrostaticPotential") : ( None, ('unit.kilojoule_per_mole')),
#("AmoebaMultipoleForce", "getElectrostaticPotential") : ( ('unit.kilojoule_per_mole'), ()), #("AmoebaMultipoleForce", "getElectrostaticPotential") : ( ('unit.kilojoule_per_mole'), ()),
("AmoebaMultipoleForce", "getElectrostaticPotential") : ( None, ()), ("AmoebaMultipoleForce", "getElectrostaticPotential") : ( None, ()),
("AmoebaMultipoleForce", "getInducedDipoles") : ( None, ()),
("AmoebaMultipoleForce", "getSystemMultipoleMoments") : ( None, ()), ("AmoebaMultipoleForce", "getSystemMultipoleMoments") : ( None, ()),
("AmoebaOutOfPlaneBendForce", "getNumOutOfPlaneBends") : ( None, ()), ("AmoebaOutOfPlaneBendForce", "getNumOutOfPlaneBends") : ( None, ()),
......
...@@ -7,15 +7,21 @@ ...@@ -7,15 +7,21 @@
int enforcePeriodic, int enforcePeriodic,
int groups) { int groups) {
State state; State state;
Py_BEGIN_ALLOW_THREADS PyThreadState* _savePythonThreadState = PyEval_SaveThread();
int types = 0; int types = 0;
if (getPositions) types |= State::Positions; if (getPositions) types |= State::Positions;
if (getVelocities) types |= State::Velocities; if (getVelocities) types |= State::Velocities;
if (getForces) types |= State::Forces; if (getForces) types |= State::Forces;
if (getEnergy) types |= State::Energy; if (getEnergy) types |= State::Energy;
if (getParameters) types |= State::Parameters; if (getParameters) types |= State::Parameters;
state = self->getState(types, enforcePeriodic, groups); try {
Py_END_ALLOW_THREADS state = self->getState(types, enforcePeriodic, groups);
}
catch (...) {
PyEval_RestoreThread(_savePythonThreadState);
throw;
}
PyEval_RestoreThread(_savePythonThreadState);
return _convertStateToLists(state); return _convertStateToLists(state);
} }
...@@ -150,15 +156,21 @@ Parameters: ...@@ -150,15 +156,21 @@ Parameters:
int enforcePeriodic, int enforcePeriodic,
int groups) { int groups) {
State state; State state;
Py_BEGIN_ALLOW_THREADS PyThreadState* _savePythonThreadState = PyEval_SaveThread();
int types = 0; int types = 0;
if (getPositions) types |= State::Positions; if (getPositions) types |= State::Positions;
if (getVelocities) types |= State::Velocities; if (getVelocities) types |= State::Velocities;
if (getForces) types |= State::Forces; if (getForces) types |= State::Forces;
if (getEnergy) types |= State::Energy; if (getEnergy) types |= State::Energy;
if (getParameters) types |= State::Parameters; if (getParameters) types |= State::Parameters;
state = self->getState(copy, types, enforcePeriodic, groups); try {
Py_END_ALLOW_THREADS state = self->getState(copy, types, enforcePeriodic, groups);
}
catch (...) {
PyEval_RestoreThread(_savePythonThreadState);
throw;
}
PyEval_RestoreThread(_savePythonThreadState);
return _convertStateToLists(state); return _convertStateToLists(state);
} }
......
/* Convert python list of tuples to C++ std::vector of Vec3 objects */ /* Convert python list of tuples to C++ std::vector of Vec3 objects */
%typemap(in) std::vector<Vec3>& (std::vector<OpenMM::Vec3> vVec) { %typemap(in) const std::vector<Vec3>& (std::vector<OpenMM::Vec3> vVec) {
// typemap -- %typemap(in) std::vector<Vec3>& (std::vector<OpenMM::Vec3> vVec) // typemap -- %typemap(in) std::vector<Vec3>& (std::vector<OpenMM::Vec3> vVec)
int i, pLength, itemLength; int i, pLength, itemLength;
double x, y, z; double x, y, z;
...@@ -34,6 +34,32 @@ ...@@ -34,6 +34,32 @@
$1 = &vVec; $1 = &vVec;
} }
/* The following two typemaps cause a non-const vector<Vec3>& to become a return value. */
%typemap(in, numinputs=0) std::vector<Vec3>& (std::vector<Vec3> temp) {
$1 = &temp;
}
%typemap(argout) std::vector<Vec3>& {
int i, n;
PyObject *pyList;
n=(*$1).size();
pyList=PyList_New(n);
PyObject* mm = PyImport_AddModule("simtk.openmm");
PyObject* vec3 = PyObject_GetAttrString(mm, "Vec3");
for (i=0; i<n; i++) {
OpenMM::Vec3& v = (*$1).at(i);
PyObject* args = Py_BuildValue("(d,d,d)", v[0], v[1], v[2]);
PyObject* pyVec = PyObject_CallObject(vec3, args);
Py_DECREF(args);
PyList_SET_ITEM(pyList, i, pyVec);
}
$result = pyList;
}
/* const vector<Vec3> should NOT become an output. */
%typemap(argout) const std::vector<Vec3>& {
}
/* Convert python tuple to C++ Vec3 object*/ /* Convert python tuple to C++ Vec3 object*/
%typemap(typecheck) Vec3 { %typemap(typecheck) Vec3 {
......
...@@ -3,6 +3,7 @@ from validateConstraints import * ...@@ -3,6 +3,7 @@ from validateConstraints import *
from simtk.openmm.app import * from simtk.openmm.app import *
from simtk.openmm import * from simtk.openmm import *
from simtk.unit import * from simtk.unit import *
import simtk.openmm.app.element as elem
class TestAmberPrmtopFile(unittest.TestCase): class TestAmberPrmtopFile(unittest.TestCase):
...@@ -93,42 +94,59 @@ class TestAmberPrmtopFile(unittest.TestCase): ...@@ -93,42 +94,59 @@ class TestAmberPrmtopFile(unittest.TestCase):
self.assertTrue(any(isinstance(f, force_type) for f in forces)) self.assertTrue(any(isinstance(f, force_type) for f in forces))
def test_ImplicitSolventParameters(self): def test_ImplicitSolventParameters(self):
"""Test that solventDielectric and soluteDielectric are passed correctly """Test that parameters are set correctly for the different types of implicit solvent."""
for the different types of implicit solvent. methodMap = {NoCutoff:NonbondedForce.NoCutoff,
CutoffNonPeriodic:NonbondedForce.CutoffNonPeriodic}
"""
for implicitSolvent_value in [HCT, OBC1, OBC2, GBn]: for implicitSolvent_value in [HCT, OBC1, OBC2, GBn]:
system = self.prmtop2.createSystem(implicitSolvent=implicitSolvent_value, for method in methodMap:
solventDielectric=50.0, system = self.prmtop2.createSystem(implicitSolvent=implicitSolvent_value,
soluteDielectric = 0.9) solventDielectric=50.0, soluteDielectric=0.9, nonbondedMethod=method)
found_matching_solvent_dielectric=False found_matching_solvent_dielectric=False
found_matching_solute_dielectric=False found_matching_solute_dielectric=False
if implicitSolvent_value in set([HCT, OBC1, GBn]): if implicitSolvent_value in set([HCT, OBC1, GBn]):
for force in system.getForces(): for force in system.getForces():
if isinstance(force, CustomGBForce): if isinstance(force, CustomGBForce):
for j in range(force.getNumGlobalParameters()): self.assertEqual(force.getNonbondedMethod(), methodMap[method])
if (force.getGlobalParameterName(j) == 'solventDielectric' and for j in range(force.getNumGlobalParameters()):
force.getGlobalParameterDefaultValue(j) == 50.0): if (force.getGlobalParameterName(j) == 'solventDielectric' and
force.getGlobalParameterDefaultValue(j) == 50.0):
found_matching_solvent_dielectric = True
if (force.getGlobalParameterName(j) == 'soluteDielectric' and
force.getGlobalParameterDefaultValue(j) == 0.9):
found_matching_solute_dielectric = True
if isinstance(force, NonbondedForce):
self.assertEqual(force.getReactionFieldDielectric(), 1.0)
self.assertEqual(force.getNonbondedMethod(), methodMap[method])
self.assertTrue(found_matching_solvent_dielectric and
found_matching_solute_dielectric)
else:
for force in system.getForces():
if isinstance(force, GBSAOBCForce):
self.assertEqual(force.getNonbondedMethod(), methodMap[method])
if force.getSolventDielectric() == 50.0:
found_matching_solvent_dielectric = True found_matching_solvent_dielectric = True
if (force.getGlobalParameterName(j) == 'soluteDielectric' and if force.getSoluteDielectric() == 0.9:
force.getGlobalParameterDefaultValue(j) == 0.9):
found_matching_solute_dielectric = True found_matching_solute_dielectric = True
if isinstance(force, NonbondedForce): if isinstance(force, NonbondedForce):
self.assertEqual(force.getReactionFieldDielectric(), 1.0) self.assertEqual(force.getReactionFieldDielectric(), 1.0)
self.assertTrue(found_matching_solvent_dielectric and self.assertEqual(force.getNonbondedMethod(), methodMap[method])
found_matching_solute_dielectric) self.assertTrue(found_matching_solvent_dielectric and
else: found_matching_solute_dielectric)
for force in system.getForces():
if isinstance(force, GBSAOBCForce): def test_HydrogenMass(self):
if force.getSolventDielectric() == 50.0: """Test that altering the mass of hydrogens works correctly."""
found_matching_solvent_dielectric = True
if force.getSoluteDielectric() == 0.9: topology = self.prmtop1.topology
found_matching_solute_dielectric = True hydrogenMass = 4*amu
if isinstance(force, NonbondedForce): system1 = self.prmtop1.createSystem()
self.assertEqual(force.getReactionFieldDielectric(), 1.0) system2 = self.prmtop1.createSystem(hydrogenMass=hydrogenMass)
self.assertTrue(found_matching_solvent_dielectric and for atom in topology.atoms():
found_matching_solute_dielectric) if atom.element == elem.hydrogen:
self.assertNotEqual(hydrogenMass, system1.getParticleMass(atom.index))
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)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
import os
import unittest
from simtk.openmm.app import *
from simtk.openmm import *
from simtk.unit import *
import simtk.openmm.app.element as elem
class TestDesmondDMSFile(unittest.TestCase):
def setUp(self):
"""Set up the tests by loading the input files."""
# alanine dipeptide with explicit water
path = os.path.join(os.path.dirname(__file__), 'systems/alanine-dipeptide-explicit-amber99SBILDN-tip3p.dms')
self.dms = DesmondDMSFile(path)
def test_NonbondedMethod(self):
"""Test all five options for the nonbondedMethod parameter."""
methodMap = {NoCutoff:NonbondedForce.NoCutoff,
CutoffNonPeriodic:NonbondedForce.CutoffNonPeriodic,
CutoffPeriodic:NonbondedForce.CutoffPeriodic,
Ewald:NonbondedForce.Ewald, PME: NonbondedForce.PME}
for method in methodMap:
system = self.dms.createSystem(nonbondedMethod=method)
forces = system.getForces()
self.assertTrue(any(isinstance(f, NonbondedForce) and
f.getNonbondedMethod()==methodMap[method]
for f in forces))
def test_Cutoff(self):
"""Test to make sure the nonbondedCutoff parameter is passed correctly."""
for method in [CutoffNonPeriodic, CutoffPeriodic, Ewald, PME]:
system = self.dms.createSystem(nonbondedMethod=method,
nonbondedCutoff=2*nanometer)
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_EwaldErrorTolerance(self):
"""Test to make sure the ewaldErrorTolerance parameter is passed correctly."""
for method in [Ewald, PME]:
system = self.dms.createSystem(nonbondedMethod=method,
ewaldErrorTolerance=1e-6)
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.dms.createSystem(removeCMMotion=b)
self.assertEqual(any(isinstance(f, CMMotionRemover) for f in system.getForces()), b)
def test_HydrogenMass(self):
"""Test that altering the mass of hydrogens works correctly."""
topology = self.dms.topology
hydrogenMass = 4*amu
system1 = self.dms.createSystem()
system2 = self.dms.createSystem(hydrogenMass=hydrogenMass)
for atom in topology.atoms():
if atom.element == elem.hydrogen:
self.assertNotEqual(hydrogenMass, system1.getParticleMass(atom.index))
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)
...@@ -3,6 +3,7 @@ from validateConstraints import * ...@@ -3,6 +3,7 @@ from validateConstraints import *
from simtk.openmm.app import * from simtk.openmm.app import *
from simtk.openmm import * from simtk.openmm import *
from simtk.unit import * from simtk.unit import *
import simtk.openmm.app.element as elem
class TestForceField(unittest.TestCase): class TestForceField(unittest.TestCase):
"""Test the ForceField.createSystem() method.""" """Test the ForceField.createSystem() method."""
...@@ -104,6 +105,20 @@ class TestForceField(unittest.TestCase): ...@@ -104,6 +105,20 @@ class TestForceField(unittest.TestCase):
self.assertTrue(found_matching_solvent_dielectric and self.assertTrue(found_matching_solvent_dielectric and
found_matching_solute_dielectric) found_matching_solute_dielectric)
def test_HydrogenMass(self):
"""Test that altering the mass of hydrogens works correctly."""
topology = self.pdb1.topology
hydrogenMass = 4*amu
system1 = self.forcefield1.createSystem(topology)
system2 = self.forcefield1.createSystem(topology, hydrogenMass=hydrogenMass)
for atom in topology.atoms():
if atom.element == elem.hydrogen:
self.assertNotEqual(hydrogenMass, system1.getParticleMass(atom.index))
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)
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -3,6 +3,7 @@ from validateConstraints import * ...@@ -3,6 +3,7 @@ from validateConstraints import *
from simtk.openmm.app import * from simtk.openmm.app import *
from simtk.openmm import * from simtk.openmm import *
from simtk.unit import * from simtk.unit import *
import simtk.openmm.app.element as elem
class TestGromacsTopFile(unittest.TestCase): class TestGromacsTopFile(unittest.TestCase):
...@@ -103,6 +104,21 @@ class TestGromacsTopFile(unittest.TestCase): ...@@ -103,6 +104,21 @@ class TestGromacsTopFile(unittest.TestCase):
self.assertTrue(found_matching_solvent_dielectric and self.assertTrue(found_matching_solvent_dielectric and
found_matching_solute_dielectric) found_matching_solute_dielectric)
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))
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)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
This source diff could not be displayed because it is too large. You can view the blob instead.
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