Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
tsoc
openmm
Commits
e0cf53ba
Commit
e0cf53ba
authored
Jul 02, 2014
by
peastman
Browse files
Began creating API for programmatically building and editing ForceFields
parent
8d4b23da
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
217 additions
and
129 deletions
+217
-129
wrappers/python/simtk/openmm/app/forcefield.py
wrappers/python/simtk/openmm/app/forcefield.py
+181
-129
wrappers/python/tests/TestForceField.py
wrappers/python/tests/TestForceField.py
+36
-0
No files found.
wrappers/python/simtk/openmm/app/forcefield.py
View file @
e0cf53ba
...
...
@@ -40,6 +40,11 @@ import simtk.unit as unit
import
element
as
elem
from
simtk.openmm.app
import
Topology
def
_convertParameterToNumber
(
param
):
if
unit
.
is_quantity
(
param
):
return
mm
.
stripUnits
((
param
,))[
0
]
return
float
(
param
)
# Enumerated values for nonbonded method
class
NoCutoff
(
object
):
...
...
@@ -121,7 +126,7 @@ class ForceField(object):
name
=
type
.
attrib
[
'name'
]
if
name
in
self
.
_atomTypes
:
raise
ValueError
(
'Found multiple definitions for atom type: '
+
name
)
self
.
_a
tomType
s
[
name
]
=
(
type
.
attrib
[
'class'
],
float
(
type
.
attrib
[
'mass'
]),
element
)
self
.
registerA
tomType
(
name
,
type
.
attrib
[
'class'
],
_convertParameterToNumber
(
type
.
attrib
[
'mass'
]),
element
)
# Load the residue templates.
...
...
@@ -129,38 +134,17 @@ class ForceField(object):
for
residue
in
root
.
find
(
'Residues'
).
findall
(
'Residue'
):
resName
=
residue
.
attrib
[
'name'
]
template
=
ForceField
.
_TemplateData
(
resName
)
self
.
_templates
[
resName
]
=
template
for
atom
in
residue
.
findall
(
'Atom'
):
template
.
atoms
.
append
(
ForceField
.
_TemplateAtomData
(
atom
.
attrib
[
'name'
],
atom
.
attrib
[
'type'
],
self
.
_atomTypes
[
atom
.
attrib
[
'type'
]][
2
]))
for
site
in
residue
.
findall
(
'VirtualSite'
):
template
.
virtualSites
.
append
(
ForceField
.
_VirtualSiteData
(
site
))
for
bond
in
residue
.
findall
(
'Bond'
):
b
=
(
int
(
bond
.
attrib
[
'from'
]),
int
(
bond
.
attrib
[
'to'
]))
template
.
bonds
.
append
(
b
)
template
.
atoms
[
b
[
0
]].
bondedTo
.
append
(
b
[
1
])
template
.
atoms
[
b
[
1
]].
bondedTo
.
append
(
b
[
0
])
template
.
addBond
(
int
(
bond
.
attrib
[
'from'
]),
int
(
bond
.
attrib
[
'to'
]))
for
bond
in
residue
.
findall
(
'ExternalBond'
):
b
=
int
(
bond
.
attrib
[
'from'
])
template
.
externalBonds
.
append
(
b
)
template
.
atoms
[
b
].
externalBonds
+=
1
for
template
in
self
.
_templates
.
values
():
signature
=
_createResidueSignature
([
atom
.
element
for
atom
in
template
.
atoms
])
if
signature
in
self
.
_templateSignatures
:
self
.
_templateSignatures
[
signature
].
append
(
template
)
else
:
self
.
_templateSignatures
[
signature
]
=
[
template
]
# Build sets of every atom type belonging to each class
for
type
in
self
.
_atomTypes
:
atomClass
=
self
.
_atomTypes
[
type
][
0
]
if
atomClass
in
self
.
_atomClasses
:
typeSet
=
self
.
_atomClasses
[
atomClass
]
else
:
typeSet
=
set
()
self
.
_atomClasses
[
atomClass
]
=
typeSet
typeSet
.
add
(
type
)
self
.
_atomClasses
[
''
].
add
(
type
)
self
.
registerResidueTemplate
(
template
)
# Load force definitions
...
...
@@ -171,12 +155,50 @@ class ForceField(object):
# Load scripts
for
node
in
tree
.
getroot
().
findall
(
'Script'
):
self
.
_scripts
.
append
(
node
.
text
)
self
.
registerScript
(
node
.
text
)
def
_findAtomTypes
(
self
,
node
,
num
):
def
getGenerators
(
self
):
"""Get the list of all registered generators."""
return
self
.
_forces
def
registerGenerator
(
self
,
generator
):
"""Register a new generator."""
self
.
_forces
.
append
(
generator
)
def
registerAtomType
(
self
,
name
,
atomClass
,
mass
,
element
):
"""Register a new atom type.
Parameters:
- name (str) The name of the new atom type
- atomClass (str) The class of the new atom type
- mass (mass) The atomic mass of the new atom type
- element (element) The element of the new atom type
"""
self
.
_atomTypes
[
name
]
=
(
atomClass
,
_convertParameterToNumber
(
mass
),
element
)
if
atomClass
in
self
.
_atomClasses
:
typeSet
=
self
.
_atomClasses
[
atomClass
]
else
:
typeSet
=
set
()
self
.
_atomClasses
[
atomClass
]
=
typeSet
typeSet
.
add
(
name
)
self
.
_atomClasses
[
''
].
add
(
name
)
def
registerResidueTemplate
(
self
,
template
):
"""Register a new residue template."""
self
.
_templates
[
template
.
name
]
=
template
signature
=
_createResidueSignature
([
atom
.
element
for
atom
in
template
.
atoms
])
if
signature
in
self
.
_templateSignatures
:
self
.
_templateSignatures
[
signature
].
append
(
template
)
else
:
self
.
_templateSignatures
[
signature
]
=
[
template
]
def
registerScript
(
self
,
script
):
"""Register a new script to be executed after building the System."""
self
.
_scripts
.
append
(
script
)
def
_findAtomTypes
(
self
,
attrib
,
num
):
"""Parse the attributes on an XML tag to find the set of atom types for each atom it involves."""
types
=
[]
attrib
=
node
.
attrib
for
i
in
range
(
num
):
if
num
==
1
:
suffix
=
''
...
...
@@ -186,7 +208,7 @@ class ForceField(object):
typeAttrib
=
'type'
+
suffix
if
classAttrib
in
attrib
:
if
typeAttrib
in
attrib
:
raise
ValueError
(
'
Tag s
pecifie
s
both a type and a class for the same atom: '
+
e
tr
ee
.
tostring
(
node
))
raise
ValueError
(
'
S
pecifie
d
both a type and a class for the same atom: '
+
s
tr
(
attrib
))
if
attrib
[
classAttrib
]
not
in
self
.
_atomClasses
:
types
.
append
(
None
)
# Unknown atom class
else
:
...
...
@@ -198,18 +220,17 @@ class ForceField(object):
types
.
append
([
attrib
[
typeAttrib
]])
return
types
def
_parseTorsion
(
self
,
node
):
def
_parseTorsion
(
self
,
attrib
):
"""Parse the node defining a torsion."""
types
=
self
.
_findAtomTypes
(
node
,
4
)
types
=
self
.
_findAtomTypes
(
attrib
,
4
)
if
None
in
types
:
return
None
torsion
=
PeriodicTorsion
(
types
)
attrib
=
node
.
attrib
index
=
1
while
'phase%d'
%
index
in
attrib
:
torsion
.
periodicity
.
append
(
int
(
attrib
[
'periodicity%d'
%
index
]))
torsion
.
phase
.
append
(
float
(
attrib
[
'phase%d'
%
index
]))
torsion
.
k
.
append
(
float
(
attrib
[
'k%d'
%
index
]))
torsion
.
phase
.
append
(
_convertParameterToNumber
(
attrib
[
'phase%d'
%
index
]))
torsion
.
k
.
append
(
_convertParameterToNumber
(
attrib
[
'k%d'
%
index
]))
index
+=
1
return
torsion
...
...
@@ -234,6 +255,11 @@ class ForceField(object):
self
.
virtualSites
=
[]
self
.
bonds
=
[]
self
.
externalBonds
=
[]
def
addBond
(
self
,
atom1
,
atom2
):
self
.
bonds
.
append
((
atom1
,
atom2
))
self
.
atoms
[
atom1
].
bondedTo
.
append
(
atom2
)
self
.
atoms
[
atom2
].
bondedTo
.
append
(
atom1
)
class
_TemplateAtomData
:
"""Inner class used to encapsulate data about an atom in a residue template definition."""
...
...
@@ -660,23 +686,27 @@ def _findMatchErrors(forcefield, res):
class
HarmonicBondGenerator
:
"""A HarmonicBondGenerator constructs a HarmonicBondForce."""
def
__init__
(
self
):
def
__init__
(
self
,
forcefield
):
self
.
ff
=
forcefield
self
.
types1
=
[]
self
.
types2
=
[]
self
.
length
=
[]
self
.
k
=
[]
def
registerBond
(
self
,
parameters
):
types
=
self
.
ff
.
_findAtomTypes
(
parameters
,
2
)
if
None
not
in
types
:
self
.
types1
.
append
(
types
[
0
])
self
.
types2
.
append
(
types
[
1
])
self
.
length
.
append
(
_convertParameterToNumber
(
parameters
[
'length'
]))
self
.
k
.
append
(
_convertParameterToNumber
(
parameters
[
'k'
]))
@
staticmethod
def
parseElement
(
element
,
ff
):
generator
=
HarmonicBondGenerator
()
ff
.
_forces
.
append
(
generator
)
generator
=
HarmonicBondGenerator
(
ff
)
ff
.
registerGenerator
(
generator
)
for
bond
in
element
.
findall
(
'Bond'
):
types
=
ff
.
_findAtomTypes
(
bond
,
2
)
if
None
not
in
types
:
generator
.
types1
.
append
(
types
[
0
])
generator
.
types2
.
append
(
types
[
1
])
generator
.
length
.
append
(
float
(
bond
.
attrib
[
'length'
]))
generator
.
k
.
append
(
float
(
bond
.
attrib
[
'k'
]))
generator
.
registerBond
(
bond
.
attrib
)
def
createForce
(
self
,
sys
,
data
,
nonbondedMethod
,
nonbondedCutoff
,
args
):
existing
=
[
sys
.
getForce
(
i
)
for
i
in
range
(
sys
.
getNumForces
())]
...
...
@@ -707,25 +737,29 @@ parsers["HarmonicBondForce"] = HarmonicBondGenerator.parseElement
class
HarmonicAngleGenerator
:
"""A HarmonicAngleGenerator constructs a HarmonicAngleForce."""
def
__init__
(
self
):
def
__init__
(
self
,
forcefield
):
self
.
ff
=
forcefield
self
.
types1
=
[]
self
.
types2
=
[]
self
.
types3
=
[]
self
.
angle
=
[]
self
.
k
=
[]
def
registerAngle
(
self
,
parameters
):
types
=
self
.
ff
.
_findAtomTypes
(
parameters
,
3
)
if
None
not
in
types
:
self
.
types1
.
append
(
types
[
0
])
self
.
types2
.
append
(
types
[
1
])
self
.
types3
.
append
(
types
[
2
])
self
.
angle
.
append
(
_convertParameterToNumber
(
parameters
[
'angle'
]))
self
.
k
.
append
(
_convertParameterToNumber
(
parameters
[
'k'
]))
@
staticmethod
def
parseElement
(
element
,
ff
):
generator
=
HarmonicAngleGenerator
()
ff
.
_forces
.
append
(
generator
)
generator
=
HarmonicAngleGenerator
(
ff
)
ff
.
registerGenerator
(
generator
)
for
angle
in
element
.
findall
(
'Angle'
):
types
=
ff
.
_findAtomTypes
(
angle
,
3
)
if
None
not
in
types
:
generator
.
types1
.
append
(
types
[
0
])
generator
.
types2
.
append
(
types
[
1
])
generator
.
types3
.
append
(
types
[
2
])
generator
.
angle
.
append
(
float
(
angle
.
attrib
[
'angle'
]))
generator
.
k
.
append
(
float
(
angle
.
attrib
[
'k'
]))
generator
.
registerAngle
(
angle
.
attrib
)
def
createForce
(
self
,
sys
,
data
,
nonbondedMethod
,
nonbondedCutoff
,
args
):
existing
=
[
sys
.
getForce
(
i
)
for
i
in
range
(
sys
.
getNumForces
())]
...
...
@@ -789,23 +823,29 @@ class PeriodicTorsion:
class
PeriodicTorsionGenerator
:
"""A PeriodicTorsionGenerator constructs a PeriodicTorsionForce."""
def
__init__
(
self
):
def
__init__
(
self
,
forcefield
):
self
.
ff
=
forcefield
self
.
proper
=
[]
self
.
improper
=
[]
def
registerProperTorsion
(
self
,
parameters
):
torsion
=
self
.
ff
.
_parseTorsion
(
parameters
)
if
torsion
is
not
None
:
self
.
proper
.
append
(
torsion
)
def
registerImproperTorsion
(
self
,
parameters
):
torsion
=
self
.
ff
.
_parseTorsion
(
parameters
)
if
torsion
is
not
None
:
self
.
improper
.
append
(
torsion
)
@
staticmethod
def
parseElement
(
element
,
ff
):
generator
=
PeriodicTorsionGenerator
()
generator
.
ff
=
ff
ff
.
_forces
.
append
(
generator
)
generator
=
PeriodicTorsionGenerator
(
ff
)
ff
.
registerGenerator
(
generator
)
for
torsion
in
element
.
findall
(
'Proper'
):
torsion
=
ff
.
_parseTorsion
(
torsion
)
if
torsion
is
not
None
:
generator
.
proper
.
append
(
torsion
)
generator
.
registerProperTorsion
(
torsion
.
attrib
)
for
torsion
in
element
.
findall
(
'Improper'
):
torsion
=
ff
.
_parseTorsion
(
torsion
)
if
torsion
is
not
None
:
generator
.
improper
.
append
(
torsion
)
generator
.
registerImproperTorsion
(
torsion
.
attrib
)
def
createForce
(
self
,
sys
,
data
,
nonbondedMethod
,
nonbondedCutoff
,
args
):
existing
=
[
sys
.
getForce
(
i
)
for
i
in
range
(
sys
.
getNumForces
())]
...
...
@@ -888,21 +928,21 @@ class RBTorsion:
class
RBTorsionGenerator
:
"""An RBTorsionGenerator constructs an RBTorsionForce."""
def
__init__
(
self
):
def
__init__
(
self
,
forcefield
):
self
.
ff
=
forcefield
self
.
proper
=
[]
self
.
improper
=
[]
@
staticmethod
def
parseElement
(
element
,
ff
):
generator
=
RBTorsionGenerator
()
generator
.
ff
=
ff
ff
.
_forces
.
append
(
generator
)
generator
=
RBTorsionGenerator
(
ff
)
ff
.
registerGenerator
(
generator
)
for
torsion
in
element
.
findall
(
'Proper'
):
types
=
ff
.
_findAtomTypes
(
torsion
,
4
)
types
=
ff
.
_findAtomTypes
(
torsion
.
attrib
,
4
)
if
None
not
in
types
:
generator
.
proper
.
append
(
RBTorsion
(
types
,
[
float
(
torsion
.
attrib
[
'c'
+
str
(
i
)])
for
i
in
range
(
6
)]))
for
torsion
in
element
.
findall
(
'Improper'
):
types
=
ff
.
_findAtomTypes
(
torsion
,
4
)
types
=
ff
.
_findAtomTypes
(
torsion
.
attrib
,
4
)
if
None
not
in
types
:
generator
.
improper
.
append
(
RBTorsion
(
types
,
[
float
(
torsion
.
attrib
[
'c'
+
str
(
i
)])
for
i
in
range
(
6
)]))
...
...
@@ -984,15 +1024,15 @@ class CMAPTorsion:
class
CMAPTorsionGenerator
:
"""A CMAPTorsionGenerator constructs a CMAPTorsionForce."""
def
__init__
(
self
):
def
__init__
(
self
,
forcefield
):
self
.
ff
=
forcefield
self
.
torsions
=
[]
self
.
maps
=
[]
@
staticmethod
def
parseElement
(
element
,
ff
):
generator
=
CMAPTorsionGenerator
()
generator
.
ff
=
ff
ff
.
_forces
.
append
(
generator
)
generator
=
CMAPTorsionGenerator
(
ff
)
ff
.
registerGenerator
(
generator
)
for
map
in
element
.
findall
(
'Map'
):
values
=
[
float
(
x
)
for
x
in
map
.
text
.
split
()]
size
=
sqrt
(
len
(
values
))
...
...
@@ -1000,7 +1040,7 @@ class CMAPTorsionGenerator:
raise
ValueError
(
'CMAP must have the same number of elements along each dimension'
)
generator
.
maps
.
append
(
values
)
for
torsion
in
element
.
findall
(
'Torsion'
):
types
=
ff
.
_findAtomTypes
(
torsion
,
5
)
types
=
ff
.
_findAtomTypes
(
torsion
.
attrib
,
5
)
if
None
not
in
types
:
generator
.
torsions
.
append
(
CMAPTorsion
(
types
,
int
(
torsion
.
attrib
[
'map'
])))
...
...
@@ -1064,28 +1104,32 @@ parsers["CMAPTorsionForce"] = CMAPTorsionGenerator.parseElement
class
NonbondedGenerator
:
"""A NonbondedGenerator constructs a NonbondedForce."""
def
__init__
(
self
,
coulomb14scale
,
lj14scale
):
def
__init__
(
self
,
forcefield
,
coulomb14scale
,
lj14scale
):
self
.
ff
=
forcefield
self
.
coulomb14scale
=
coulomb14scale
self
.
lj14scale
=
lj14scale
self
.
typeMap
=
{}
def
registerAtom
(
self
,
parameters
):
types
=
self
.
ff
.
_findAtomTypes
(
parameters
,
1
)
if
None
not
in
types
:
values
=
(
_convertParameterToNumber
(
parameters
[
'charge'
]),
_convertParameterToNumber
(
parameters
[
'sigma'
]),
_convertParameterToNumber
(
parameters
[
'epsilon'
]))
for
t
in
types
[
0
]:
self
.
typeMap
[
t
]
=
values
@
staticmethod
def
parseElement
(
element
,
ff
):
existing
=
[
f
for
f
in
ff
.
_forces
if
isinstance
(
f
,
NonbondedGenerator
)]
if
len
(
existing
)
==
0
:
generator
=
NonbondedGenerator
(
float
(
element
.
attrib
[
'coulomb14scale'
]),
float
(
element
.
attrib
[
'lj14scale'
]))
ff
.
_forces
.
append
(
generator
)
generator
=
NonbondedGenerator
(
ff
,
float
(
element
.
attrib
[
'coulomb14scale'
]),
float
(
element
.
attrib
[
'lj14scale'
]))
ff
.
registerGenerator
(
generator
)
else
:
# Multiple <NonbondedForce> tags were found, probably in different files. Simply add more types to the existing one.
generator
=
existing
[
0
]
if
generator
.
coulomb14scale
!=
float
(
element
.
attrib
[
'coulomb14scale'
])
or
generator
.
lj14scale
!=
float
(
element
.
attrib
[
'lj14scale'
]):
raise
ValueError
(
'Found multiple NonbondedForce tags with different 1-4 scales'
)
for
atom
in
element
.
findall
(
'Atom'
):
types
=
ff
.
_findAtomTypes
(
atom
,
1
)
if
None
not
in
types
:
values
=
(
float
(
atom
.
attrib
[
'charge'
]),
float
(
atom
.
attrib
[
'sigma'
]),
float
(
atom
.
attrib
[
'epsilon'
]))
for
t
in
types
[
0
]:
generator
.
typeMap
[
t
]
=
values
generator
.
registerAtom
(
atom
.
attrib
)
def
createForce
(
self
,
sys
,
data
,
nonbondedMethod
,
nonbondedCutoff
,
args
):
methodMap
=
{
NoCutoff
:
mm
.
NonbondedForce
.
NoCutoff
,
...
...
@@ -1149,24 +1193,28 @@ parsers["NonbondedForce"] = NonbondedGenerator.parseElement
class
GBSAOBCGenerator
:
"""A GBSAOBCGenerator constructs a GBSAOBCForce."""
def
__init__
(
self
):
def
__init__
(
self
,
forcefield
):
self
.
ff
=
forcefield
self
.
typeMap
=
{}
def
registerAtom
(
self
,
parameters
):
types
=
self
.
ff
.
_findAtomTypes
(
parameters
,
1
)
if
None
not
in
types
:
values
=
(
_convertParameterToNumber
(
parameters
[
'charge'
]),
_convertParameterToNumber
(
parameters
[
'radius'
]),
_convertParameterToNumber
(
parameters
[
'scale'
]))
for
t
in
types
[
0
]:
self
.
typeMap
[
t
]
=
values
@
staticmethod
def
parseElement
(
element
,
ff
):
existing
=
[
f
for
f
in
ff
.
_forces
if
isinstance
(
f
,
GBSAOBCGenerator
)]
if
len
(
existing
)
==
0
:
generator
=
GBSAOBCGenerator
()
ff
.
_forces
.
append
(
generator
)
generator
=
GBSAOBCGenerator
(
ff
)
ff
.
registerGenerator
(
generator
)
else
:
# Multiple <GBSAOBCForce> tags were found, probably in different files. Simply add more types to the existing one.
generator
=
existing
[
0
]
for
atom
in
element
.
findall
(
'Atom'
):
types
=
ff
.
_findAtomTypes
(
atom
,
1
)
if
None
not
in
types
:
values
=
(
float
(
atom
.
attrib
[
'charge'
]),
float
(
atom
.
attrib
[
'radius'
]),
float
(
atom
.
attrib
[
'scale'
]))
for
t
in
types
[
0
]:
generator
.
typeMap
[
t
]
=
values
generator
.
registerAtom
(
atom
.
attrib
)
def
createForce
(
self
,
sys
,
data
,
nonbondedMethod
,
nonbondedCutoff
,
args
):
methodMap
=
{
NoCutoff
:
mm
.
NonbondedForce
.
NoCutoff
,
...
...
@@ -1206,7 +1254,6 @@ class GBVIGenerator:
"""A GBVIGenerator constructs a GBVIForce."""
def
__init__
(
self
,
ff
):
self
.
ff
=
ff
self
.
fixedParameters
=
{}
self
.
fixedParameters
[
'soluteDielectric'
]
=
1.0
...
...
@@ -1224,9 +1271,9 @@ class GBVIGenerator:
if
(
key
in
element
.
attrib
):
generator
.
fixedParameters
[
key
]
=
float
(
element
.
attrib
[
key
])
ff
.
_forces
.
append
(
generator
)
ff
.
registerGenerator
(
generator
)
for
atom
in
element
.
findall
(
'Atom'
):
types
=
ff
.
_findAtomTypes
(
atom
,
1
)
types
=
ff
.
_findAtomTypes
(
atom
.
attrib
,
1
)
if
None
not
in
types
:
values
=
(
float
(
atom
.
attrib
[
'charge'
]),
float
(
atom
.
attrib
[
'radius'
]),
float
(
atom
.
attrib
[
'gamma'
]))
for
t
in
types
[
0
]:
...
...
@@ -1292,7 +1339,8 @@ parsers["GBVIForce"] = GBVIGenerator.parseElement
class
CustomBondGenerator
:
"""A CustomBondGenerator constructs a CustomBondForce."""
def
__init__
(
self
):
def
__init__
(
self
,
forcefield
):
self
.
ff
=
forcefield
self
.
types1
=
[]
self
.
types2
=
[]
self
.
globalParams
=
{}
...
...
@@ -1301,15 +1349,15 @@ class CustomBondGenerator:
@
staticmethod
def
parseElement
(
element
,
ff
):
generator
=
CustomBondGenerator
()
ff
.
_forces
.
append
(
generator
)
generator
=
CustomBondGenerator
(
ff
)
ff
.
registerGenerator
(
generator
)
generator
.
energy
=
element
.
attrib
[
'energy'
]
for
param
in
element
.
findall
(
'GlobalParameter'
):
generator
.
globalParams
[
param
.
attrib
[
'name'
]]
=
float
(
param
.
attrib
[
'defaultValue'
])
for
param
in
element
.
findall
(
'PerBondParameter'
):
generator
.
perBondParams
.
append
(
param
.
attrib
[
'name'
])
for
bond
in
element
.
findall
(
'Bond'
):
types
=
ff
.
_findAtomTypes
(
bond
,
2
)
types
=
ff
.
_findAtomTypes
(
bond
.
attrib
,
2
)
if
None
not
in
types
:
generator
.
types1
.
append
(
types
[
0
])
generator
.
types2
.
append
(
types
[
1
])
...
...
@@ -1339,7 +1387,8 @@ parsers["CustomBondForce"] = CustomBondGenerator.parseElement
class
CustomAngleGenerator
:
"""A CustomAngleGenerator constructs a CustomAngleForce."""
def
__init__
(
self
):
def
__init__
(
self
,
forcefield
):
self
.
ff
=
forcefield
self
.
types1
=
[]
self
.
types2
=
[]
self
.
types3
=
[]
...
...
@@ -1349,15 +1398,15 @@ class CustomAngleGenerator:
@
staticmethod
def
parseElement
(
element
,
ff
):
generator
=
CustomAngleGenerator
()
ff
.
_forces
.
append
(
generator
)
generator
=
CustomAngleGenerator
(
ff
)
ff
.
registerGenerator
(
generator
)
generator
.
energy
=
element
.
attrib
[
'energy'
]
for
param
in
element
.
findall
(
'GlobalParameter'
):
generator
.
globalParams
[
param
.
attrib
[
'name'
]]
=
float
(
param
.
attrib
[
'defaultValue'
])
for
param
in
element
.
findall
(
'PerAngleParameter'
):
generator
.
perAngleParams
.
append
(
param
.
attrib
[
'name'
])
for
angle
in
element
.
findall
(
'Angle'
):
types
=
ff
.
_findAtomTypes
(
angle
,
3
)
types
=
ff
.
_findAtomTypes
(
angle
.
attrib
,
3
)
if
None
not
in
types
:
generator
.
types1
.
append
(
types
[
0
])
generator
.
types2
.
append
(
types
[
1
])
...
...
@@ -1401,7 +1450,8 @@ class CustomTorsion:
class
CustomTorsionGenerator
:
"""A CustomTorsionGenerator constructs a CustomTorsionForce."""
def
__init__
(
self
):
def
__init__
(
self
,
forcefield
):
self
.
ff
=
forcefield
self
.
proper
=
[]
self
.
improper
=
[]
self
.
globalParams
=
{}
...
...
@@ -1409,20 +1459,19 @@ class CustomTorsionGenerator:
@
staticmethod
def
parseElement
(
element
,
ff
):
generator
=
CustomTorsionGenerator
()
generator
.
ff
=
ff
ff
.
_forces
.
append
(
generator
)
generator
=
CustomTorsionGenerator
(
ff
)
ff
.
registerGenerator
(
generator
)
generator
.
energy
=
element
.
attrib
[
'energy'
]
for
param
in
element
.
findall
(
'GlobalParameter'
):
generator
.
globalParams
[
param
.
attrib
[
'name'
]]
=
float
(
param
.
attrib
[
'defaultValue'
])
for
param
in
element
.
findall
(
'PerTorsionParameter'
):
generator
.
perTorsionParams
.
append
(
param
.
attrib
[
'name'
])
for
torsion
in
element
.
findall
(
'Proper'
):
types
=
ff
.
_findAtomTypes
(
torsion
,
4
)
types
=
ff
.
_findAtomTypes
(
torsion
.
attrib
,
4
)
if
None
not
in
types
:
generator
.
proper
.
append
(
CustomTorsion
(
types
,
[
float
(
torsion
.
attrib
[
param
])
for
param
in
generator
.
perTorsionParams
]))
for
torsion
in
element
.
findall
(
'Improper'
):
types
=
ff
.
_findAtomTypes
(
torsion
,
4
)
types
=
ff
.
_findAtomTypes
(
torsion
.
attrib
,
4
)
if
None
not
in
types
:
generator
.
improper
.
append
(
CustomTorsion
(
types
,
[
float
(
torsion
.
attrib
[
param
])
for
param
in
generator
.
perTorsionParams
]))
...
...
@@ -1491,7 +1540,8 @@ parsers["CustomTorsionForce"] = CustomTorsionGenerator.parseElement
class
CustomNonbondedGenerator
:
"""A CustomNonbondedGenerator constructs a CustomNonbondedForce."""
def
__init__
(
self
,
energy
,
bondCutoff
):
def
__init__
(
self
,
forcefield
,
energy
,
bondCutoff
):
self
.
ff
=
forcefield
self
.
energy
=
energy
self
.
bondCutoff
=
bondCutoff
self
.
typeMap
=
{}
...
...
@@ -1501,14 +1551,14 @@ class CustomNonbondedGenerator:
@
staticmethod
def
parseElement
(
element
,
ff
):
generator
=
CustomNonbondedGenerator
(
element
.
attrib
[
'energy'
],
int
(
element
.
attrib
[
'bondCutoff'
]))
ff
.
_forces
.
append
(
generator
)
generator
=
CustomNonbondedGenerator
(
ff
,
element
.
attrib
[
'energy'
],
int
(
element
.
attrib
[
'bondCutoff'
]))
ff
.
registerGenerator
(
generator
)
for
param
in
element
.
findall
(
'GlobalParameter'
):
generator
.
globalParams
[
param
.
attrib
[
'name'
]]
=
float
(
param
.
attrib
[
'defaultValue'
])
for
param
in
element
.
findall
(
'PerParticleParameter'
):
generator
.
perParticleParams
.
append
(
param
.
attrib
[
'name'
])
for
atom
in
element
.
findall
(
'Atom'
):
types
=
ff
.
_findAtomTypes
(
atom
,
1
)
types
=
ff
.
_findAtomTypes
(
atom
.
attrib
,
1
)
if
None
not
in
types
:
values
=
[
float
(
atom
.
attrib
[
param
])
for
param
in
generator
.
perParticleParams
]
for
t
in
types
[
0
]:
...
...
@@ -1589,7 +1639,8 @@ parsers["CustomNonbondedForce"] = CustomNonbondedGenerator.parseElement
class
CustomGBGenerator
:
"""A CustomGBGenerator constructs a CustomGBForce."""
def
__init__
(
self
):
def
__init__
(
self
,
forcefield
):
self
.
ff
=
forcefield
self
.
typeMap
=
{}
self
.
globalParams
=
{}
self
.
perParticleParams
=
[]
...
...
@@ -1599,14 +1650,14 @@ class CustomGBGenerator:
@
staticmethod
def
parseElement
(
element
,
ff
):
generator
=
CustomGBGenerator
()
ff
.
_forces
.
append
(
generator
)
generator
=
CustomGBGenerator
(
ff
)
ff
.
registerGenerator
(
generator
)
for
param
in
element
.
findall
(
'GlobalParameter'
):
generator
.
globalParams
[
param
.
attrib
[
'name'
]]
=
float
(
param
.
attrib
[
'defaultValue'
])
for
param
in
element
.
findall
(
'PerParticleParameter'
):
generator
.
perParticleParams
.
append
(
param
.
attrib
[
'name'
])
for
atom
in
element
.
findall
(
'Atom'
):
types
=
ff
.
_findAtomTypes
(
atom
,
1
)
types
=
ff
.
_findAtomTypes
(
atom
.
attrib
,
1
)
if
None
not
in
types
:
values
=
[
float
(
atom
.
attrib
[
param
])
for
param
in
generator
.
perParticleParams
]
for
t
in
types
[
0
]:
...
...
@@ -1729,7 +1780,7 @@ class AmoebaBondGenerator:
generator
=
AmoebaBondGenerator
(
float
(
element
.
attrib
[
'bond-cubic'
]),
float
(
element
.
attrib
[
'bond-quartic'
]))
forceField
.
_forces
.
append
(
generator
)
for
bond
in
element
.
findall
(
'Bond'
):
types
=
forceField
.
_findAtomTypes
(
bond
,
2
)
types
=
forceField
.
_findAtomTypes
(
bond
.
attrib
,
2
)
if
None
not
in
types
:
generator
.
types1
.
append
(
types
[
0
])
generator
.
types2
.
append
(
types
[
1
])
...
...
@@ -1842,7 +1893,7 @@ class AmoebaAngleGenerator:
generator
=
AmoebaAngleGenerator
(
forceField
,
float
(
element
.
attrib
[
'angle-cubic'
]),
float
(
element
.
attrib
[
'angle-quartic'
]),
float
(
element
.
attrib
[
'angle-pentic'
]),
float
(
element
.
attrib
[
'angle-sextic'
]))
forceField
.
_forces
.
append
(
generator
)
for
angle
in
element
.
findall
(
'Angle'
):
types
=
forceField
.
_findAtomTypes
(
angle
,
3
)
types
=
forceField
.
_findAtomTypes
(
angle
.
attrib
,
3
)
if
None
not
in
types
:
generator
.
types1
.
append
(
types
[
0
])
...
...
@@ -2327,7 +2378,7 @@ class AmoebaTorsionGenerator:
# where ti=[amplitude_i,angle_i]
for
torsion
in
element
.
findall
(
'Torsion'
):
types
=
forceField
.
_findAtomTypes
(
torsion
,
4
)
types
=
forceField
.
_findAtomTypes
(
torsion
.
attrib
,
4
)
if
None
not
in
types
:
generator
.
types1
.
append
(
types
[
0
])
...
...
@@ -2438,7 +2489,7 @@ class AmoebaPiTorsionGenerator:
forceField
.
_forces
.
append
(
generator
)
for
piTorsion
in
element
.
findall
(
'PiTorsion'
):
types
=
forceField
.
_findAtomTypes
(
piTorsion
,
2
)
types
=
forceField
.
_findAtomTypes
(
piTorsion
.
attrib
,
2
)
if
None
not
in
types
:
generator
.
types1
.
append
(
types
[
0
])
generator
.
types2
.
append
(
types
[
1
])
...
...
@@ -2557,7 +2608,7 @@ class AmoebaTorsionTorsionGenerator:
# <TorsionTorsion class1="3" class2="1" class3="2" class4="3" class5="1" grid="0" nx="25" ny="25" />
for
torsionTorsion
in
element
.
findall
(
'TorsionTorsion'
):
types
=
forceField
.
_findAtomTypes
(
torsionTorsion
,
5
)
types
=
forceField
.
_findAtomTypes
(
torsionTorsion
.
attrib
,
5
)
if
None
not
in
types
:
generator
.
types1
.
append
(
types
[
0
])
...
...
@@ -2795,7 +2846,7 @@ class AmoebaStretchBendGenerator:
# <StretchBend class1="2" class2="1" class3="4" k1="3.14005676385" k2="3.14005676385" />
for
stretchBend
in
element
.
findall
(
'StretchBend'
):
types
=
forceField
.
_findAtomTypes
(
stretchBend
,
3
)
types
=
forceField
.
_findAtomTypes
(
stretchBend
.
attrib
,
3
)
if
None
not
in
types
:
generator
.
types1
.
append
(
types
[
0
])
...
...
@@ -2952,7 +3003,7 @@ class AmoebaVdwGenerator:
# sigma is modified based on radiustype and radiussize
for
atom
in
element
.
findall
(
'Vdw'
):
types
=
forceField
.
_findAtomTypes
(
atom
,
1
)
types
=
forceField
.
_findAtomTypes
(
atom
.
attrib
,
1
)
if
None
not
in
types
:
values
=
[
float
(
atom
.
attrib
[
'sigma'
]),
float
(
atom
.
attrib
[
'epsilon'
]),
float
(
atom
.
attrib
[
'reduction'
])]
...
...
@@ -3225,7 +3276,7 @@ class AmoebaMultipoleGenerator:
# set type map: [ kIndices, multipoles, AMOEBA/OpenMM axis type]
for
atom
in
element
.
findall
(
'Multipole'
):
types
=
forceField
.
_findAtomTypes
(
atom
,
1
)
types
=
forceField
.
_findAtomTypes
(
atom
.
attrib
,
1
)
if
None
not
in
types
:
# k-indices not provided default to 0
...
...
@@ -3282,7 +3333,7 @@ class AmoebaMultipoleGenerator:
# polarization parameters
for
atom
in
element
.
findall
(
'Polarize'
):
types
=
forceField
.
_findAtomTypes
(
atom
,
1
)
types
=
forceField
.
_findAtomTypes
(
atom
.
attrib
,
1
)
if
None
not
in
types
:
classIndex
=
atom
.
attrib
[
'type'
]
...
...
@@ -3810,7 +3861,7 @@ class AmoebaWcaDispersionGenerator:
# typeMap[] = [ radius, epsilon ]
for
atom
in
element
.
findall
(
'WcaDispersion'
):
types
=
forceField
.
_findAtomTypes
(
atom
,
1
)
types
=
forceField
.
_findAtomTypes
(
atom
.
attrib
,
1
)
if
None
not
in
types
:
values
=
[
float
(
atom
.
attrib
[
'radius'
]),
float
(
atom
.
attrib
[
'epsilon'
])]
...
...
@@ -4154,7 +4205,7 @@ class AmoebaUreyBradleyGenerator:
generator
=
AmoebaUreyBradleyGenerator
()
forceField
.
_forces
.
append
(
generator
)
for
bond
in
element
.
findall
(
'UreyBradley'
):
types
=
forceField
.
_findAtomTypes
(
bond
,
3
)
types
=
forceField
.
_findAtomTypes
(
bond
.
attrib
,
3
)
if
None
not
in
types
:
generator
.
types1
.
append
(
types
[
0
])
...
...
@@ -4205,20 +4256,21 @@ parsers["AmoebaUreyBradleyForce"] = AmoebaUreyBradleyGenerator.parseElement
class
DrudeGenerator
:
"""A DrudeGenerator constructs a DrudeForce."""
def
__init__
(
self
):
def
__init__
(
self
,
forcefield
):
self
.
ff
=
forcefield
self
.
typeMap
=
{}
@
staticmethod
def
parseElement
(
element
,
ff
):
existing
=
[
f
for
f
in
ff
.
_forces
if
isinstance
(
f
,
DrudeGenerator
)]
if
len
(
existing
)
==
0
:
generator
=
DrudeGenerator
()
ff
.
_forces
.
append
(
generator
)
generator
=
DrudeGenerator
(
ff
)
ff
.
registerGenerator
(
generator
)
else
:
# Multiple <DrudeForce> tags were found, probably in different files. Simply add more types to the existing one.
generator
=
existing
[
0
]
for
particle
in
element
.
findall
(
'Particle'
):
types
=
ff
.
_findAtomTypes
(
particle
,
5
)
types
=
ff
.
_findAtomTypes
(
particle
.
attrib
,
5
)
if
None
not
in
types
[:
2
]:
aniso12
=
0.0
aniso34
=
0.0
...
...
wrappers/python/tests/TestForceField.py
View file @
e0cf53ba
...
...
@@ -4,6 +4,7 @@ from simtk.openmm.app import *
from
simtk.openmm
import
*
from
simtk.unit
import
*
import
simtk.openmm.app.element
as
elem
import
simtk.openmm.app.forcefield
as
forcefield
class
TestForceField
(
unittest
.
TestCase
):
"""Test the ForceField.createSystem() method."""
...
...
@@ -149,6 +150,41 @@ class TestForceField(unittest.TestCase):
if
diff
>
0.1
and
diff
/
norm
(
f1
)
>
1e-3
:
numDifferences
+=
1
self
.
assertTrue
(
numDifferences
<
system
.
getNumParticles
()
/
20
)
# Tolerate occasional differences from numerical error
def
test_ProgrammaticForceField
(
self
):
"""Test building a ForceField programmatically."""
# Build the ForceField for TIP3P programmatically.
ff
=
ForceField
()
ff
.
registerAtomType
(
'tip3p-O'
,
'OW'
,
15.99943
*
daltons
,
elem
.
oxygen
)
ff
.
registerAtomType
(
'tip3p-H'
,
'HW'
,
1.007947
*
daltons
,
elem
.
hydrogen
)
residue
=
ForceField
.
_TemplateData
(
'HOH'
)
residue
.
atoms
.
append
(
ForceField
.
_TemplateAtomData
(
'O'
,
'tip3p-O'
,
elem
.
oxygen
))
residue
.
atoms
.
append
(
ForceField
.
_TemplateAtomData
(
'H1'
,
'tip3p-H'
,
elem
.
hydrogen
))
residue
.
atoms
.
append
(
ForceField
.
_TemplateAtomData
(
'H2'
,
'tip3p-H'
,
elem
.
hydrogen
))
residue
.
addBond
(
0
,
1
)
residue
.
addBond
(
0
,
2
)
ff
.
registerResidueTemplate
(
residue
)
bonds
=
forcefield
.
HarmonicBondGenerator
(
ff
)
bonds
.
registerBond
({
'class1'
:
'OW'
,
'class2'
:
'HW'
,
'length'
:
0.09572
*
nanometers
,
'k'
:
462750.4
*
kilojoules_per_mole
/
nanometer
})
ff
.
registerGenerator
(
bonds
)
angles
=
forcefield
.
HarmonicAngleGenerator
(
ff
)
angles
.
registerAngle
({
'class1'
:
'HW'
,
'class2'
:
'OW'
,
'class3'
:
'HW'
,
'angle'
:
1.82421813418
*
radians
,
'k'
:
836.8
*
kilojoules_per_mole
/
radian
})
ff
.
registerGenerator
(
angles
)
nonbonded
=
forcefield
.
NonbondedGenerator
(
ff
,
0.833333
,
0.5
)
nonbonded
.
registerAtom
({
'type'
:
'tip3p-O'
,
'charge'
:
-
0.834
,
'sigma'
:
0.31507524065751241
*
nanometers
,
'epsilon'
:
0.635968
*
kilojoules_per_mole
})
nonbonded
.
registerAtom
({
'type'
:
'tip3p-H'
,
'charge'
:
0.417
,
'sigma'
:
1
*
nanometers
,
'epsilon'
:
0
*
kilojoules_per_mole
})
ff
.
registerGenerator
(
nonbonded
)
# Build a water box.
modeller
=
Modeller
(
Topology
(),
[])
modeller
.
addSolvent
(
ff
,
boxSize
=
Vec3
(
3
,
3
,
3
)
*
nanometers
)
# Create a system using the programmatic force field as well as one from an XML file.
system1
=
ff
.
createSystem
(
modeller
.
topology
)
ff2
=
ForceField
(
'tip3p.xml'
)
system2
=
ff2
.
createSystem
(
modeller
.
topology
)
self
.
assertEqual
(
XmlSerializer
.
serialize
(
system1
),
XmlSerializer
.
serialize
(
system2
))
class
AmoebaTestForceField
(
unittest
.
TestCase
):
"""Test the ForceField.createSystem() method with the AMOEBA forcefield."""
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment