Commit cc03f111 authored by Jason Swails's avatar Jason Swails
Browse files

Add support for flexibleConstraints to ForceField.createSystem

There are times when it is helpful to have the energy terms actually
added to the System even if that degree of freedom is constrained. Other
parsers (notably the Amber parsers) already support this keyword, so add
that support to ForceField as well

This also replaces some string-based element detection algorithms with
whatever the PDB parser does (by comparing directly to atom.element)
parent b317bd38
...@@ -223,7 +223,7 @@ class ForceField(object): ...@@ -223,7 +223,7 @@ class ForceField(object):
trees.append(tree) trees.append(tree)
# Process includes. # Process includes.
for parentFile, tree in zip(files, trees): for parentFile, tree in zip(files, trees):
if isinstance(parentFile, str): if isinstance(parentFile, str):
parentDir = os.path.dirname(parentFile) parentDir = os.path.dirname(parentFile)
...@@ -1035,6 +1035,8 @@ class ForceField(object): ...@@ -1035,6 +1035,8 @@ class ForceField(object):
not terminated properly. This option can create ambiguities where multiple not terminated properly. This option can create ambiguities where multiple
templates match the same residue. If that happens, use the residueTemplates templates match the same residue. If that happens, use the residueTemplates
argument to specify which one to use. argument to specify which one to use.
flexibleConstraints : boolean=False
If True, parameters for constrained degrees of freedom will be added to the System
args args
Arbitrary additional keyword arguments may also be specified. Arbitrary additional keyword arguments may also be specified.
This allows extra parameters to be specified that are specific to This allows extra parameters to be specified that are specific to
...@@ -1140,9 +1142,9 @@ class ForceField(object): ...@@ -1140,9 +1142,9 @@ class ForceField(object):
if not unit.is_quantity(hydrogenMass): if not unit.is_quantity(hydrogenMass):
hydrogenMass *= unit.dalton hydrogenMass *= unit.dalton
for atom1, atom2 in topology.bonds(): for atom1, atom2 in topology.bonds():
if atom1.element == elem.hydrogen: if atom1.element is elem.hydrogen:
(atom1, atom2) = (atom2, atom1) (atom1, atom2) = (atom2, atom1)
if atom2.element == elem.hydrogen and atom1.element not in (elem.hydrogen, None): if atom2.element is elem.hydrogen and atom1.element not in (elem.hydrogen, None):
transferMass = hydrogenMass-sys.getParticleMass(atom2.index) transferMass = hydrogenMass-sys.getParticleMass(atom2.index)
sys.setParticleMass(atom2.index, hydrogenMass) sys.setParticleMass(atom2.index, hydrogenMass)
sys.setParticleMass(atom1.index, sys.getParticleMass(atom1.index)-transferMass) sys.setParticleMass(atom1.index, sys.getParticleMass(atom1.index)-transferMass)
...@@ -1208,7 +1210,7 @@ class ForceField(object): ...@@ -1208,7 +1210,7 @@ class ForceField(object):
for bond in data.bonds: for bond in data.bonds:
atom1 = data.atoms[bond.atom1] atom1 = data.atoms[bond.atom1]
atom2 = data.atoms[bond.atom2] atom2 = data.atoms[bond.atom2]
bond.isConstrained = atom1.name.startswith('H') or atom2.name.startswith('H') bond.isConstrained = atom1.element is elem.hydrogen or atom2.element is elem.hydrogen
if rigidWater: if rigidWater:
for bond in data.bonds: for bond in data.bonds:
atom1 = data.atoms[bond.atom1] atom1 = data.atoms[bond.atom1]
...@@ -1224,11 +1226,11 @@ class ForceField(object): ...@@ -1224,11 +1226,11 @@ class ForceField(object):
atom2 = data.atoms[angle[1]] atom2 = data.atoms[angle[1]]
atom3 = data.atoms[angle[2]] atom3 = data.atoms[angle[2]]
numH = 0 numH = 0
if atom1.name.startswith('H'): if atom1.element is elem.hydrogen:
numH += 1 numH += 1
if atom3.name.startswith('H'): if atom3.element is elem.hydrogen:
numH += 1 numH += 1
data.isAngleConstrained.append(numH == 2 or (numH == 1 and atom2.name.startswith('O'))) data.isAngleConstrained.append(numH == 2 or (numH == 1 and atom2.element is elem.oxygen))
else: else:
data.isAngleConstrained = len(data.angles)*[False] data.isAngleConstrained = len(data.angles)*[False]
if rigidWater: if rigidWater:
...@@ -1897,8 +1899,11 @@ class HarmonicBondGenerator(object): ...@@ -1897,8 +1899,11 @@ class HarmonicBondGenerator(object):
bond.length = self.length[i] bond.length = self.length[i]
if bond.isConstrained: if bond.isConstrained:
data.addConstraint(sys, bond.atom1, bond.atom2, self.length[i]) data.addConstraint(sys, bond.atom1, bond.atom2, self.length[i])
elif self.k[i] != 0: if self.k[i] != 0:
force.addBond(bond.atom1, bond.atom2, self.length[i], self.k[i]) # flexibleConstraints allows us to add parameters even if the DOF is
# constrained
if not bond.isConstrained or args.get('flexibleConstraints', False):
force.addBond(bond.atom1, bond.atom2, self.length[i], self.k[i])
break break
parsers["HarmonicBondForce"] = HarmonicBondGenerator.parseElement parsers["HarmonicBondForce"] = HarmonicBondGenerator.parseElement
...@@ -1978,8 +1983,9 @@ class HarmonicAngleGenerator(object): ...@@ -1978,8 +1983,9 @@ class HarmonicAngleGenerator(object):
if l1 is not None and l2 is not None: if l1 is not None and l2 is not None:
length = sqrt(l1*l1 + l2*l2 - 2*l1*l2*cos(self.angle[i])) length = sqrt(l1*l1 + l2*l2 - 2*l1*l2*cos(self.angle[i]))
data.addConstraint(sys, angle[0], angle[2], length) data.addConstraint(sys, angle[0], angle[2], length)
elif self.k[i] != 0: if self.k[i] != 0:
force.addAngle(angle[0], angle[1], angle[2], self.angle[i], self.k[i]) if not isConstrained or args.get('flexibleConstraints', False):
force.addAngle(angle[0], angle[1], angle[2], self.angle[i], self.k[i])
break break
parsers["HarmonicAngleForce"] = HarmonicAngleGenerator.parseElement parsers["HarmonicAngleForce"] = HarmonicAngleGenerator.parseElement
...@@ -3160,8 +3166,9 @@ class AmoebaBondGenerator(object): ...@@ -3160,8 +3166,9 @@ class AmoebaBondGenerator(object):
bond.length = self.length[i] bond.length = self.length[i]
if bond.isConstrained: if bond.isConstrained:
data.addConstraint(sys, bond.atom1, bond.atom2, self.length[i]) data.addConstraint(sys, bond.atom1, bond.atom2, self.length[i])
elif self.k[i] != 0: if self.k[i] != 0:
force.addBond(bond.atom1, bond.atom2, self.length[i], self.k[i]) if not bond.isConstrained or args.get('flexibleConstraints', False):
force.addBond(bond.atom1, bond.atom2, self.length[i], self.k[i])
break break
parsers["AmoebaBondForce"] = AmoebaBondGenerator.parseElement parsers["AmoebaBondForce"] = AmoebaBondGenerator.parseElement
...@@ -3288,6 +3295,8 @@ class AmoebaAngleGenerator(object): ...@@ -3288,6 +3295,8 @@ class AmoebaAngleGenerator(object):
force.setAmoebaGlobalAnglePentic(self.pentic) force.setAmoebaGlobalAnglePentic(self.pentic)
force.setAmoebaGlobalAngleSextic(self.sextic) force.setAmoebaGlobalAngleSextic(self.sextic)
DEG_TO_RAD = math.pi / 180
for angleDict in angleList: for angleDict in angleList:
angle = angleDict['angle'] angle = angleDict['angle']
isConstrained = angleDict['isConstrained'] isConstrained = angleDict['isConstrained']
...@@ -3302,8 +3311,8 @@ class AmoebaAngleGenerator(object): ...@@ -3302,8 +3311,8 @@ class AmoebaAngleGenerator(object):
if (type1 in types1 and type2 in types2 and type3 in types3) or (type1 in types3 and type2 in types2 and type3 in types1): if (type1 in types1 and type2 in types2 and type3 in types3) or (type1 in types3 and type2 in types2 and type3 in types1):
if isConstrained and self.k[i] != 0.0: if isConstrained and self.k[i] != 0.0:
angleDict['idealAngle'] = self.angle[i][0] angleDict['idealAngle'] = self.angle[i][0]
addAngleConstraint(angle, self.angle[i][0]*math.pi/180.0, data, sys) addAngleConstraint(angle, self.angle[i][0]*DEG_TO_RAD, data, sys)
elif self.k[i] != 0: if self.k[i] != 0 and (not isConstrained or args.get('flexibleConstraints', False)):
lenAngle = len(self.angle[i]) lenAngle = len(self.angle[i])
if (lenAngle > 1): if (lenAngle > 1):
# get k-index by counting number of non-angle hydrogens on the central atom # get k-index by counting number of non-angle hydrogens on the central atom
...@@ -3372,7 +3381,7 @@ class AmoebaAngleGenerator(object): ...@@ -3372,7 +3381,7 @@ class AmoebaAngleGenerator(object):
angleDict['idealAngle'] = self.angle[i][0] angleDict['idealAngle'] = self.angle[i][0]
if (isConstrained and self.k[i] != 0.0): if (isConstrained and self.k[i] != 0.0):
addAngleConstraint(angle, self.angle[i][0]*math.pi/180.0, data, sys) addAngleConstraint(angle, self.angle[i][0]*math.pi/180.0, data, sys)
else: if self.k[i] != 0.0 and (not isConstrained or args.get('flexibleConstraints', False)):
force.addAngle(angle[0], angle[1], angle[2], angle[3], self.angle[i][0], self.k[i]) force.addAngle(angle[0], angle[1], angle[2], angle[3], self.angle[i][0], self.k[i])
break break
...@@ -5431,7 +5440,7 @@ class AmoebaUreyBradleyGenerator(object): ...@@ -5431,7 +5440,7 @@ class AmoebaUreyBradleyGenerator(object):
force = existing[0] force = existing[0]
for (angle, isConstrained) in zip(data.angles, data.isAngleConstrained): for (angle, isConstrained) in zip(data.angles, data.isAngleConstrained):
if (isConstrained): if (isConstrained and not args.get('flexibleConstraints', False)):
continue continue
type1 = data.atomType[data.atoms[angle[0]]] type1 = data.atomType[data.atoms[angle[0]]]
type2 = data.atomType[data.atoms[angle[1]]] type2 = data.atomType[data.atoms[angle[1]]]
......
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