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
a8457c6b
Commit
a8457c6b
authored
Mar 06, 2025
by
Evan Pretti
Browse files
Check all wildcard combinations for dihedrals and impropers, and add test
parent
bda67c67
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
149 additions
and
23 deletions
+149
-23
wrappers/python/openmm/app/charmmpsffile.py
wrappers/python/openmm/app/charmmpsffile.py
+32
-23
wrappers/python/tests/TestCharmmFiles.py
wrappers/python/tests/TestCharmmFiles.py
+117
-0
No files found.
wrappers/python/openmm/app/charmmpsffile.py
View file @
a8457c6b
...
...
@@ -34,6 +34,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
from
__future__
import
division
,
absolute_import
,
print_function
from
functools
import
wraps
from
itertools
import
combinations
from
math
import
pi
,
cos
,
sin
,
sqrt
import
os
import
re
...
...
@@ -603,9 +604,9 @@ class CharmmPsfFile(object):
- If any parameters that are necessary cannot be found, a
MissingParameter exception is raised.
- If any dihedral or improper parameters cannot be found, I will try
inserting wildcards (
at either end for dihedrals and as the two
central atoms in impropers) and see if that matches. Wild-cards
will apply ONLY if specific parameters cannot be
found.
inserting wildcards (
in up to three positions) and see if that
matches. Wild-cards will apply ONLY if specific parameters cannot be
found.
- This method will expand the dihedral_parameter_list attribute by
adding a separate Dihedral object for each term for types that
have a multi-term expansion
...
...
@@ -655,16 +656,11 @@ class CharmmPsfFile(object):
for
dih
in
self
.
dihedral_list
:
# Store the atoms
a1
,
a2
,
a3
,
a4
=
dih
.
atom1
,
dih
.
atom2
,
dih
.
atom3
,
dih
.
atom4
at1
,
at2
,
at3
,
at4
=
a1
.
attype
,
a2
.
attype
,
a3
.
attype
,
a4
.
attype
# First see if the exact dihedral is specified
key
=
min
((
at1
,
at2
,
at3
,
at4
),
(
at4
,
at3
,
at2
,
at1
))
if
not
key
in
parmset
.
dihedral_types
:
# Check for wild-cards
key
=
min
((
'X'
,
at2
,
at3
,
'X'
),
(
'X'
,
at3
,
at2
,
'X'
))
if
not
key
in
parmset
.
dihedral_types
:
raise
MissingParameter
(
'No dihedral parameters found for '
'%r'
%
dih
)
dtlist
=
parmset
.
dihedral_types
[
key
]
try
:
at_list
=
[
a1
.
attype
,
a2
.
attype
,
a3
.
attype
,
a4
.
attype
]
dtlist
=
_match_with_wildcards
(
at_list
,
parmset
.
dihedral_types
,
3
)
except
KeyError
:
raise
MissingParameter
(
'No dihedral parameters found for %r'
%
dih
)
for
i
,
dt
in
enumerate
(
dtlist
):
self
.
dihedral_parameter_list
.
append
(
Dihedral
(
a1
,
a2
,
a3
,
a4
,
dt
))
# See if we include the end-group interactions for this
...
...
@@ -678,18 +674,11 @@ class CharmmPsfFile(object):
for
imp
in
self
.
improper_list
:
# Store the atoms
a1
,
a2
,
a3
,
a4
=
imp
.
atom1
,
imp
.
atom2
,
imp
.
atom3
,
imp
.
atom4
at1
,
at2
,
at3
,
at4
=
a1
.
attype
,
a2
.
attype
,
a3
.
attype
,
a4
.
attype
key
=
min
((
at1
,
at2
,
at3
,
at4
),
(
at4
,
at3
,
at2
,
at1
))
if
not
key
in
parmset
.
improper_types
:
key
=
min
((
at1
,
'X'
,
'X'
,
at4
),(
at4
,
'X'
,
'X'
,
at1
))
if
not
key
in
parmset
.
improper_types
:
raise
MissingParameter
(
'No improper dihedral parameters found for '
'%r'
%
imp
)
try
:
imp
.
improper_type
=
parmset
.
improper_types
[
key
]
at_list
=
[
a1
.
attype
,
a2
.
attype
,
a3
.
attype
,
a4
.
attype
]
imp
.
improper_type
=
_match_with_wildcards
(
at_list
,
parmset
.
improper_types
,
3
)
except
KeyError
:
raise
MissingParameter
(
'No improper parameters found for %r'
%
imp
)
raise
MissingParameter
(
'No improper parameters found for %r'
%
imp
)
# Now do the cmaps. These will not have wild-cards
for
cmap
in
self
.
cmap_list
:
# Store the atoms for easy reference
...
...
@@ -1854,6 +1843,26 @@ def _mbondi3_radii(atom_list):
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def
_match_with_wildcards
(
at_list
,
type_dict
,
max_wc_count
):
"""
Tries to find a match for at_list in type_dict. If one can't be found,
tries to insert up to max_wc_count wildcards in different combinations.
Assumes that for a given key, min(key, key[::-1]) is the one in type_dict.
"""
for
wc_count
in
range
(
max_wc_count
+
1
):
for
wc_indices
in
combinations
(
range
(
len
(
at_list
)),
wc_count
):
key
=
list
(
at_list
)
for
wc_index
in
wc_indices
:
key
[
wc_index
]
=
'X'
key
=
tuple
(
key
)
key
=
min
(
key
,
key
[::
-
1
])
if
key
in
type_dict
:
return
type_dict
[
key
]
raise
KeyError
(
key
)
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
if
__name__
==
'__main__'
:
import
doctest
doctest
.
testmod
()
wrappers/python/tests/TestCharmmFiles.py
View file @
a8457c6b
...
...
@@ -4,6 +4,7 @@ from openmm.app import *
from
openmm
import
*
from
openmm.unit
import
*
import
openmm.app.element
as
elem
import
itertools
import
math
import
os
import
tempfile
...
...
@@ -382,6 +383,122 @@ class TestCharmmFiles(unittest.TestCase):
dtheta
=
math
.
pi
-
angle
self
.
assertAlmostEqual
(
energy
,
dtheta
**
2
,
delta
=
1e-5
)
def
test_TorsionWildcards
(
self
):
"""Test matching of dihedrals and impropers with wildcards."""
for
test_improper
,
wild_1
,
wild_2
,
wild_3
,
wild_4
,
reverse
,
want_mismatch
in
itertools
.
product
((
False
,
True
),
repeat
=
7
):
# Test with up to 3 wildcards.
if
wild_1
and
wild_2
and
wild_3
and
wild_4
:
continue
# Test both dihedrals and impropers. If want_mismatch is set, make
# sure that the non-wildcard atoms ensure no match to the torsion.
prm_header
=
'IMPH'
if
test_improper
else
'DIHE'
type_1
=
'X'
if
wild_1
else
(
'C2'
if
want_mismatch
else
'C1'
)
type_2
=
'X'
if
wild_2
else
(
'C1'
if
want_mismatch
else
'C2'
)
type_3
=
'X'
if
wild_3
else
(
'C4'
if
want_mismatch
else
'C3'
)
type_4
=
'X'
if
wild_4
else
(
'C3'
if
want_mismatch
else
'C4'
)
if
reverse
:
type_1
,
type_2
,
type_3
,
type_4
=
type_4
,
type_3
,
type_2
,
type_1
psf_dihedral
=
''
if
test_improper
else
f
'
{
1
:
10
}{
2
:
10
}{
3
:
10
}{
4
:
10
}
'
psf_improper
=
f
'
{
1
:
10
}{
2
:
10
}{
3
:
10
}{
4
:
10
}
'
if
test_improper
else
''
with
tempfile
.
TemporaryDirectory
()
as
temp_path
:
prm_path
=
os
.
path
.
join
(
temp_path
,
'test.prm'
)
psf_path
=
os
.
path
.
join
(
temp_path
,
'test.psf'
)
# Write a sample PRM file.
with
open
(
prm_path
,
'w'
)
as
prm_file
:
print
(
f
"""*TEST
*
ATOMS
MASS -1 C1 12.0110
MASS -1 C2 12.0110
MASS -1 C3 12.0110
MASS -1 C4 12.0110
BOND
C1 C2 1 1
C
{
1
if
test_improper
else
2
}
C3 1 1
C
{
1
if
test_improper
else
3
}
C4 1 1
{
prm_header
}
{
type_1
}
{
type_2
}
{
type_3
}
{
type_4
}
1 1 0
NBON
C1 0 0 1
C2 0 0 1
C3 0 0 1
C4 0 0 1
END"""
,
file
=
prm_file
)
# Write a sample PSF file.
with
open
(
psf_path
,
'w'
)
as
psf_file
:
print
(
f
"""PSF EXT CMAP CHEQ XPLOR
1 !NTITLE
* TEST
4 !NATOM
1 TEST 1 TEST1 C1 C1 0.000000 12.0110 0
2 TEST 1 TEST1 C2 C2 0.000000 12.0110 0
3 TEST 1 TEST1 C3 C3 0.000000 12.0110 0
4 TEST 1 TEST1 C4 C4 0.000000 12.0110 0
3 !NBOND: bonds
{
1
:
10
}{
2
:
10
}{
1
if
test_improper
else
2
:
10
}{
3
:
10
}{
1
if
test_improper
else
3
:
10
}{
4
:
10
}
0 !NTHETA: angles
{
0
if
test_improper
else
1
:
10
}
!NPHI: dihedrals
{
psf_dihedral
}
{
1
if
test_improper
else
0
:
10
}
!NIMPHI: impropers
{
psf_improper
}
0 !NDON: donors
0 !NACC: acceptors
0 !NNB
0 0 0 0
1 0 !NGRP NST2
0 0 0
1 !MOLNT
1 1 1 1
0 0 !NUMLP NUMLPH
0 !NCRTERM
"""
,
file
=
psf_file
)
prm
=
CharmmParameterSet
(
prm_path
)
psf
=
CharmmPsfFile
(
psf_path
)
if
want_mismatch
:
# Make sure that the system doesn't get parameterized.
with
self
.
assertRaises
(
internal
.
charmm
.
exceptions
.
MissingParameter
):
system
=
psf
.
createSystem
(
prm
)
else
:
# Make sure that one dihedral or improper gets added.
system
=
psf
.
createSystem
(
prm
)
force_type
=
CustomTorsionForce
if
test_improper
else
PeriodicTorsionForce
force
,
=
(
force
for
force
in
system
.
getForces
()
if
isinstance
(
force
,
force_type
))
assert
force
.
getNumTorsions
()
==
1
assert
force
.
getTorsionParameters
(
0
)[:
4
]
==
[
0
,
1
,
2
,
3
]
def
test_Residues
(
self
):
"""Test that residues are read correctly, even if they have the same RESID while being in separate segments."""
m14
=
([
"C{}"
.
format
(
i
)
for
i
in
range
(
1
,
14
)]
...
...
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