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
b33a7a5a
Unverified
Commit
b33a7a5a
authored
Feb 27, 2022
by
Peter Eastman
Committed by
GitHub
Feb 27, 2022
Browse files
Added option for box shape when building solvent boxes (#3480)
parent
952d3402
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
56 additions
and
22 deletions
+56
-22
wrappers/python/openmm/app/modeller.py
wrappers/python/openmm/app/modeller.py
+33
-13
wrappers/python/tests/TestModeller.py
wrappers/python/tests/TestModeller.py
+23
-9
No files found.
wrappers/python/openmm/app/modeller.py
View file @
b33a7a5a
...
@@ -6,7 +6,7 @@ Simbios, the NIH National Center for Physics-Based Simulation of
...
@@ -6,7 +6,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-202
1
Stanford University and the Authors.
Portions copyright (c) 2012-202
2
Stanford University and the Authors.
Authors: Peter Eastman
Authors: Peter Eastman
Contributors:
Contributors:
...
@@ -48,7 +48,7 @@ import random
...
@@ -48,7 +48,7 @@ import random
import
sys
import
sys
import
xml.etree.ElementTree
as
etree
import
xml.etree.ElementTree
as
etree
from
copy
import
deepcopy
from
copy
import
deepcopy
from
math
import
ceil
,
floor
from
math
import
ceil
,
floor
,
sqrt
from
collections
import
defaultdict
from
collections
import
defaultdict
class
Modeller
(
object
):
class
Modeller
(
object
):
...
@@ -375,8 +375,8 @@ class Modeller(object):
...
@@ -375,8 +375,8 @@ class Modeller(object):
self
.
topology
=
modeller
.
topology
self
.
topology
=
modeller
.
topology
self
.
positions
=
modeller
.
positions
self
.
positions
=
modeller
.
positions
def
addSolvent
(
self
,
forcefield
,
model
=
'tip3p'
,
boxSize
=
None
,
boxVectors
=
None
,
padding
=
None
,
numAdded
=
None
,
positiveIon
=
'Na+'
,
negativeIon
=
'Cl-'
,
ionicStrength
=
0
*
molar
,
neutralize
=
True
):
def
addSolvent
(
self
,
forcefield
,
model
=
'tip3p'
,
boxSize
=
None
,
boxVectors
=
None
,
padding
=
None
,
numAdded
=
None
,
boxShape
=
'cube'
,
positiveIon
=
'Na+'
,
negativeIon
=
'Cl-'
,
ionicStrength
=
0
*
molar
,
neutralize
=
True
):
"""Add solvent (both water and ions) to the model to fill a
rectangular
box.
"""Add solvent (both water and ions) to the model to fill a
periodic
box.
The algorithm works as follows:
The algorithm works as follows:
...
@@ -390,12 +390,18 @@ class Modeller(object):
...
@@ -390,12 +390,18 @@ class Modeller(object):
1. You can explicitly give the vectors defining the periodic box to use.
1. You can explicitly give the vectors defining the periodic box to use.
2. Alternatively, for a rectangular box you can simply give the dimensions of the unit cell.
2. Alternatively, for a rectangular box you can simply give the dimensions of the unit cell.
3. You can give a padding distance. The largest dimension of the solute (along the x, y, or z axis) is determined, and a cubic
3. You can give a padding distance. A bounding sphere containing the solute is determined, and the box size is
box of size (largest dimension)+2*padding is used.
set to (sphere radius)+(padding). This guarantees no atom in the solute will come closer than the padding
4. You can specify the total number of molecules (both waters and ions) to add. A cubic box is then created whose size is
distance to any atom of another periodic copy.
4. You can specify the total number of molecules (both waters and ions) to add. A box is then created whose size is
just large enough hold the specified amount of solvent.
just large enough hold the specified amount of solvent.
5. Finally, if none of the above options is specified, the existing Topology's box vectors are used.
5. Finally, if none of the above options is specified, the existing Topology's box vectors are used.
When specifying either a padding distance or a number of molecules, you can specify a shape for the periodic box:
cubic, rhombic dodecahedron, or truncated octahedron. Using a non-rectangular box allows the same distance
between periodic copies to be achieved with a smaller box. The most compact option is a rhombic dodecahedron,
for which the box volume is 70.7% the volume of a cubic box with the same amount of padding.
Parameters
Parameters
----------
----------
forcefield : ForceField
forcefield : ForceField
...
@@ -410,6 +416,9 @@ class Modeller(object):
...
@@ -410,6 +416,9 @@ class Modeller(object):
the padding distance to use
the padding distance to use
numAdded : int=None
numAdded : int=None
the total number of molecules (waters and ions) to add
the total number of molecules (waters and ions) to add
boxShape: str='cube'
the box shape to use. Allowed values are 'cube', 'dodecahedron', and 'octahedron'. If padding and numAdded
are both None, this is ignored.
positiveIon : string='Na+'
positiveIon : string='Na+'
the type of positive ion to add. Allowed values are 'Cs+', 'K+', 'Li+', 'Na+', and 'Rb+'
the type of positive ion to add. Allowed values are 'Cs+', 'K+', 'Li+', 'Na+', and 'Rb+'
negativeIon : string='Cl-'
negativeIon : string='Cl-'
...
@@ -449,7 +458,7 @@ class Modeller(object):
...
@@ -449,7 +458,7 @@ class Modeller(object):
if
numAdded
is
not
None
:
if
numAdded
is
not
None
:
# Select a padding distance which is guaranteed to give more than the specified number of molecules.
# Select a padding distance which is guaranteed to give more than the specified number of molecules.
padding
=
1.1
*
(
numAdded
/
((
len
(
pdbResidues
)
/
pdbBoxSize
[
0
]
**
3
)
*
8
))
**
(
1.0
/
3.0
)
padding
=
2.2
*
(
numAdded
/
((
len
(
pdbResidues
)
/
pdbBoxSize
[
0
]
**
3
)
*
8
))
**
(
1.0
/
3.0
)
if
padding
<
0.5
:
if
padding
<
0.5
:
padding
=
0.5
# Ensure we have enough when adding very small numbers of molecules
padding
=
0.5
# Ensure we have enough when adding very small numbers of molecules
if
boxVectors
is
not
None
:
if
boxVectors
is
not
None
:
...
@@ -466,12 +475,23 @@ class Modeller(object):
...
@@ -466,12 +475,23 @@ class Modeller(object):
if
is_quantity
(
padding
):
if
is_quantity
(
padding
):
padding
=
padding
.
value_in_unit
(
nanometer
)
padding
=
padding
.
value_in_unit
(
nanometer
)
if
len
(
self
.
positions
)
==
0
:
if
len
(
self
.
positions
)
==
0
:
maxSize
=
0
radius
=
0
else
:
positions
=
self
.
positions
.
value_in_unit
(
nanometer
)
minRange
=
Vec3
(
*
(
min
((
pos
[
i
]
for
pos
in
positions
))
for
i
in
range
(
3
)))
maxRange
=
Vec3
(
*
(
max
((
pos
[
i
]
for
pos
in
positions
))
for
i
in
range
(
3
)))
center
=
0.5
*
(
minRange
+
maxRange
)
radius
=
max
(
unit
.
norm
(
center
-
pos
)
for
pos
in
positions
)
width
=
2
*
radius
+
padding
box
=
width
*
Vec3
(
1
,
1
,
1
)
if
boxShape
==
'cube'
:
vectors
=
(
Vec3
(
width
,
0
,
0
),
Vec3
(
0
,
width
,
0
),
Vec3
(
0
,
0
,
width
))
elif
boxShape
==
'dodecahedron'
:
vectors
=
(
Vec3
(
width
,
0
,
0
),
Vec3
(
0
,
width
,
0
),
Vec3
(
0.5
,
0.5
,
0.5
*
sqrt
(
2
))
*
width
)
elif
boxShape
==
'octahedron'
:
vectors
=
(
Vec3
(
width
,
0
,
0
),
Vec3
(
1
/
3
,
2
*
sqrt
(
2
)
/
3
,
0
)
*
width
,
Vec3
(
-
1
/
3
,
sqrt
(
2
)
/
3
,
sqrt
(
6
)
/
3
)
*
width
)
else
:
else
:
maxSize
=
max
(
max
((
pos
[
i
]
for
pos
in
self
.
positions
))
-
min
((
pos
[
i
]
for
pos
in
self
.
positions
))
for
i
in
range
(
3
))
raise
ValueError
(
f
'Illegal box shape:
{
boxShape
}
'
)
maxSize
=
maxSize
.
value_in_unit
(
nanometer
)
box
=
(
maxSize
+
2
*
padding
)
*
Vec3
(
1
,
1
,
1
)
vectors
=
(
Vec3
(
maxSize
+
2
*
padding
,
0
,
0
),
Vec3
(
0
,
maxSize
+
2
*
padding
,
0
),
Vec3
(
0
,
0
,
maxSize
+
2
*
padding
))
else
:
else
:
box
=
self
.
topology
.
getUnitCellDimensions
().
value_in_unit
(
nanometer
)
box
=
self
.
topology
.
getUnitCellDimensions
().
value_in_unit
(
nanometer
)
vectors
=
self
.
topology
.
getPeriodicBoxVectors
().
value_in_unit
(
nanometer
)
vectors
=
self
.
topology
.
getPeriodicBoxVectors
().
value_in_unit
(
nanometer
)
...
...
wrappers/python/tests/TestModeller.py
View file @
b33a7a5a
from
collections
import
defaultdict
from
collections
import
defaultdict
import
unittest
import
unittest
import
math
import
sys
from
validateModeller
import
*
from
validateModeller
import
*
from
openmm.app
import
*
from
openmm.app
import
*
...
@@ -333,7 +331,6 @@ class TestModeller(unittest.TestCase):
...
@@ -333,7 +331,6 @@ class TestModeller(unittest.TestCase):
self
.
assertVecAlmostEqual
(
dim3
[
2
]
/
nanometers
,
Vec3
(
0
,
0
,
5.5
))
self
.
assertVecAlmostEqual
(
dim3
[
2
]
/
nanometers
,
Vec3
(
0
,
0
,
5.5
))
# Second way of passing in the periodic box vectors: with the boxSize parameter to addSolvent()
# Second way of passing in the periodic box vectors: with the boxSize parameter to addSolvent()
topology_start
=
self
.
pdb
.
topology
modeller
=
Modeller
(
topology_start
,
self
.
positions
)
modeller
=
Modeller
(
topology_start
,
self
.
positions
)
modeller
.
deleteWater
()
modeller
.
deleteWater
()
modeller
.
addSolvent
(
self
.
forcefield
,
boxSize
=
Vec3
(
3.6
,
4.6
,
5.6
)
*
nanometers
)
modeller
.
addSolvent
(
self
.
forcefield
,
boxSize
=
Vec3
(
3.6
,
4.6
,
5.6
)
*
nanometers
)
...
@@ -345,7 +342,6 @@ class TestModeller(unittest.TestCase):
...
@@ -345,7 +342,6 @@ class TestModeller(unittest.TestCase):
self
.
assertVecAlmostEqual
(
dim3
[
2
]
/
nanometers
,
Vec3
(
0
,
0
,
5.6
))
self
.
assertVecAlmostEqual
(
dim3
[
2
]
/
nanometers
,
Vec3
(
0
,
0
,
5.6
))
# Third way of passing in the periodic box vectors: with the boxVectors parameter to addSolvent()
# Third way of passing in the periodic box vectors: with the boxVectors parameter to addSolvent()
topology_start
=
self
.
pdb
.
topology
modeller
=
Modeller
(
topology_start
,
self
.
positions
)
modeller
=
Modeller
(
topology_start
,
self
.
positions
)
modeller
.
deleteWater
()
modeller
.
deleteWater
()
modeller
.
addSolvent
(
self
.
forcefield
,
boxVectors
=
(
Vec3
(
3.4
,
0
,
0
),
Vec3
(
0.5
,
4.4
,
0
),
Vec3
(
-
1.0
,
-
1.5
,
5.4
))
*
nanometers
)
modeller
.
addSolvent
(
self
.
forcefield
,
boxVectors
=
(
Vec3
(
3.4
,
0
,
0
),
Vec3
(
0.5
,
4.4
,
0
),
Vec3
(
-
1.0
,
-
1.5
,
5.4
))
*
nanometers
)
...
@@ -357,25 +353,43 @@ class TestModeller(unittest.TestCase):
...
@@ -357,25 +353,43 @@ class TestModeller(unittest.TestCase):
self
.
assertVecAlmostEqual
(
dim3
[
2
]
/
nanometers
,
Vec3
(
-
1.0
,
-
1.5
,
5.4
))
self
.
assertVecAlmostEqual
(
dim3
[
2
]
/
nanometers
,
Vec3
(
-
1.0
,
-
1.5
,
5.4
))
# Fourth way of passing in the periodic box vectors: pass a 'padding' value to addSolvent()
# Fourth way of passing in the periodic box vectors: pass a 'padding' value to addSolvent()
topology_start
=
self
.
pdb
.
topology
modeller
=
Modeller
(
topology_start
,
self
.
positions
)
modeller
=
Modeller
(
topology_start
,
self
.
positions
)
modeller
.
deleteWater
()
modeller
.
deleteWater
()
modeller
.
addSolvent
(
self
.
forcefield
,
padding
=
1.0
*
nanometers
)
modeller
.
addSolvent
(
self
.
forcefield
,
padding
=
1.0
*
nanometers
)
topology_after
=
modeller
.
getTopology
()
topology_after
=
modeller
.
getTopology
()
dim3
=
topology_after
.
getPeriodicBoxVectors
()
dim3
=
topology_after
.
getPeriodicBoxVectors
()
self
.
assertVecAlmostEqual
(
dim3
[
0
]
/
nanometers
,
Vec3
(
2.8802
,
0
,
0
))
self
.
assertVecAlmostEqual
(
dim3
[
0
]
/
nanometers
,
Vec3
(
1.924363
,
0
,
0
))
self
.
assertVecAlmostEqual
(
dim3
[
1
]
/
nanometers
,
Vec3
(
0
,
2.8802
,
0
))
self
.
assertVecAlmostEqual
(
dim3
[
1
]
/
nanometers
,
Vec3
(
0
,
1.924363
,
0
))
self
.
assertVecAlmostEqual
(
dim3
[
2
]
/
nanometers
,
Vec3
(
0
,
0
,
2.8802
))
self
.
assertVecAlmostEqual
(
dim3
[
2
]
/
nanometers
,
Vec3
(
0
,
0
,
1.924363
))
# Fifth way: specify a number of molecules to add instead of a box size
# Fifth way: specify a number of molecules to add instead of a box size
topology_start
=
self
.
pdb
.
topology
modeller
=
Modeller
(
topology_start
,
self
.
positions
)
modeller
=
Modeller
(
topology_start
,
self
.
positions
)
modeller
.
deleteWater
()
modeller
.
deleteWater
()
numInitial
=
len
(
list
(
modeller
.
topology
.
residues
()))
numInitial
=
len
(
list
(
modeller
.
topology
.
residues
()))
modeller
.
addSolvent
(
self
.
forcefield
,
numAdded
=
1000
)
modeller
.
addSolvent
(
self
.
forcefield
,
numAdded
=
1000
)
self
.
assertEqual
(
numInitial
+
1000
,
len
(
list
(
modeller
.
topology
.
residues
())))
self
.
assertEqual
(
numInitial
+
1000
,
len
(
list
(
modeller
.
topology
.
residues
())))
def
test_addSolventBoxShape
(
self
):
"""Test the addSolvent() method; test the different box shapes."""
modeller
=
Modeller
(
self
.
pdb
.
topology
,
self
.
positions
)
modeller
.
deleteWater
()
modeller
.
addSolvent
(
self
.
forcefield
,
padding
=
1.0
*
nanometers
,
boxShape
=
'cube'
)
cubeVectors
=
modeller
.
getTopology
().
getPeriodicBoxVectors
()
modeller
=
Modeller
(
self
.
pdb
.
topology
,
self
.
positions
)
modeller
.
deleteWater
()
modeller
.
addSolvent
(
self
.
forcefield
,
padding
=
1.0
*
nanometers
,
boxShape
=
'dodecahedron'
)
dodecVectors
=
modeller
.
getTopology
().
getPeriodicBoxVectors
()
modeller
=
Modeller
(
self
.
pdb
.
topology
,
self
.
positions
)
modeller
.
deleteWater
()
modeller
.
addSolvent
(
self
.
forcefield
,
padding
=
1.0
*
nanometers
,
boxShape
=
'octahedron'
)
octVectors
=
modeller
.
getTopology
().
getPeriodicBoxVectors
()
cubeVolume
=
cubeVectors
[
0
][
0
]
*
cubeVectors
[
1
][
1
]
*
cubeVectors
[
2
][
2
]
/
(
nanometers
**
3
)
dodecVolume
=
dodecVectors
[
0
][
0
]
*
dodecVectors
[
1
][
1
]
*
dodecVectors
[
2
][
2
]
/
(
nanometers
**
3
)
octVolume
=
octVectors
[
0
][
0
]
*
octVectors
[
1
][
1
]
*
octVectors
[
2
][
2
]
/
(
nanometers
**
3
)
self
.
assertAlmostEqual
(
0.707
,
dodecVolume
/
cubeVolume
,
places
=
3
)
self
.
assertAlmostEqual
(
0.770
,
octVolume
/
cubeVolume
,
places
=
3
)
def
test_addSolventNeutralSolvent
(
self
):
def
test_addSolventNeutralSolvent
(
self
):
""" Test the addSolvent() method; test adding ions to neutral solvent. """
""" Test the addSolvent() method; test adding ions to neutral solvent. """
...
...
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