"vscode:/vscode.git/clone" did not exist on "551c5b03af19cdce1860f3ed39d27054acf20dd1"
Commit 895f9a6f authored by Jason Swails's avatar Jason Swails
Browse files

Update the CHARMM API to be consistent with the rest of OpenMM.

parent 2e927a35
......@@ -220,7 +220,7 @@ class CharmmParameterSet(object):
atomic_number = get_by_symbol(elem).atomic_number
except (IndexError, KeyError):
# Figure it out from the mass
atomic_number = element_by_mass(mass).atomic_number
atomic_number = Element.getByMass(mass).atomic_number
atype = AtomType(name=name, number=idx, mass=mass,
atomic_number=atomic_number)
self.atom_types_str[atype.name] = atype
......@@ -441,7 +441,7 @@ class CharmmParameterSet(object):
atomic_number = get_by_symbol(elem).atomic_number
except (IndexError, KeyError):
# Figure it out from the mass
atomic_number = element_by_mass(mass).atomic_number
atomic_number = Element.getByMass(mass).atomic_number
atype = AtomType(name=name, number=idx, mass=mass,
atomic_number=atomic_number)
self.atom_types_str[atype.name] = atype
......@@ -488,7 +488,7 @@ class CharmmParameterSet(object):
time.
Example:
>>> params = CharmmParameterSet.loadSet(pfile='charmm.prm').condense()
>>> params = CharmmParameterSet('charmm.prm').condense()
"""
# First scan through all of the bond types
self._condense_types(self.bond_types)
......@@ -529,18 +529,3 @@ class CharmmParameterSet(object):
typedict[key2] = typedict[key1]
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def element_by_mass(mass):
""" Determines what element the given atom is based on its mass """
diff = mass
best_guess = 'EP'
for key in Element._elements_by_atomic_number:
element = Element._elements_by_atomic_number[key]
massdiff = abs(element.mass.value_in_unit(u.daltons) - mass)
if massdiff < diff:
best_guess = element
diff = massdiff
return best_guess
......@@ -53,7 +53,7 @@ def _catchindexerror(func):
class CharmmPsfFile(object):
"""
A chemical structure instantiated from CHARMM files. You can instantiate a
ProteinStructure from a PSF file using the load_from_psf constructor
CharmmPsfFile from a PSF file using the load_from_psf constructor
Example:
>>> cs = CharmmPsfFile("testfiles/test.psf")
......@@ -80,7 +80,7 @@ class CharmmPsfFile(object):
etc.)
Example:
>>> cs = ProteinStructure.load_from_psf("testfiles/test.psf")
>>> cs = CharmmPsfFile("testfiles/test.psf")
>>> len(cs.atom_list)
33
>>> len(cs.bond_list)
......@@ -102,8 +102,8 @@ class CharmmPsfFile(object):
@_catchindexerror
def __init__(self, psf_name):
"""
Opens and parses a PSF file, then instantiates a ProteinStructure
instance from the data.
Opens and parses a PSF file, then instantiates a CharmmPsfFile instance
from the data.
Parameters:
psf_name (str) : Name of the PSF file (it must exist)
......@@ -112,7 +112,7 @@ class CharmmPsfFile(object):
IOError : If file "psf_name" does not exist
CharmmPSFError: If any parsing errors are encountered
"""
conv = ProteinStructure._convert
conv = CharmmPsfFile._convert
# Make sure the file exists
if not os.path.exists(psf_name):
raise IOError('Could not find PSF file %s' % psf_name)
......@@ -164,7 +164,7 @@ class CharmmPsfFile(object):
# Eat the next line
psf.readline()
# Now get the number of bonds
nbond, holder = ProteinStructure._parse_psf_section(psf, int)
nbond, holder = CharmmPsfFile._parse_psf_section(psf, int)
bond_list = TrackedList()
if len(holder) != nbond * 2:
raise CharmmPSFError('Got %d indexes for %d bonds' %
......@@ -175,7 +175,7 @@ class CharmmPsfFile(object):
bond_list.append(Bond(atom_list[id1], atom_list[id2]))
bond_list.changed = False
# Now get the number of angles and the angle list
ntheta, holder = ProteinStructure._parse_psf_section(psf, int)
ntheta, holder = CharmmPsfFile._parse_psf_section(psf, int)
angle_list = TrackedList()
if len(holder) != ntheta * 3:
raise CharmmPSFError('Got %d indexes for %d angles' %
......@@ -189,7 +189,7 @@ class CharmmPsfFile(object):
)
angle_list.changed = False
# Now get the number of torsions and the torsion list
nphi, holder = ProteinStructure._parse_psf_section(psf, int)
nphi, holder = CharmmPsfFile._parse_psf_section(psf, int)
dihedral_list = TrackedList()
if len(holder) != nphi * 4:
raise CharmmPSFError('Got %d indexes for %d torsions' %
......@@ -205,7 +205,7 @@ class CharmmPsfFile(object):
)
dihedral_list.changed = False
# Now get the number of improper torsions
nimphi, holder = ProteinStructure._parse_psf_section(psf, int)
nimphi, holder = CharmmPsfFile._parse_psf_section(psf, int)
improper_list = TrackedList()
if len(holder) != nimphi * 4:
raise CharmmPSFError('Got %d indexes for %d impropers' %
......@@ -221,7 +221,7 @@ class CharmmPsfFile(object):
)
improper_list.changed = False
# Now handle the donors (what is this used for??)
ndon, holder = ProteinStructure._parse_psf_section(psf, int)
ndon, holder = CharmmPsfFile._parse_psf_section(psf, int)
donor_list = TrackedList()
if len(holder) != ndon * 2:
raise CharmmPSFError('Got %d indexes for %d donors' %
......@@ -232,7 +232,7 @@ class CharmmPsfFile(object):
donor_list.append(AcceptorDonor(atom_list[id1], atom_list[id2]))
donor_list.changed = False
# Now handle the acceptors (what is this used for??)
nacc, holder = ProteinStructure._parse_psf_section(psf, int)
nacc, holder = CharmmPsfFile._parse_psf_section(psf, int)
acceptor_list = TrackedList()
if len(holder) != nacc * 2:
raise CharmmPSFError('Got %d indexes for %d acceptors' %
......@@ -244,9 +244,9 @@ class CharmmPsfFile(object):
acceptor_list.changed = False
# Now get the NNB section. Not sure what this section is for or what it
# does...
nnb, holder = ProteinStructure._parse_psf_section(psf, int)
nnb, holder = CharmmPsfFile._parse_psf_section(psf, int)
# Now get the group sections
pointers, holder = ProteinStructure._parse_psf_section(psf, int)
pointers, holder = CharmmPsfFile._parse_psf_section(psf, int)
group_list = TrackedList()
try:
ngrp, nst2 = pointers
......@@ -273,7 +273,7 @@ class CharmmPsfFile(object):
# and the number of entries in the group. If the # of entries is
# NATOM, assume we have MOLNT section. Warn if the MOLNT section is
# 'wrong'...
pointer, holder = ProteinStructure._parse_psf_section(psf, int)
pointer, holder = CharmmPsfFile._parse_psf_section(psf, int)
# Assign all of the atoms to molecules recursively
set_molecules(atom_list)
......@@ -292,7 +292,7 @@ class CharmmPsfFile(object):
'section.')
psf.readline() # blank
# Now we get to the cross-term section
ncrterm, holder = ProteinStructure._parse_psf_section(psf, int)
ncrterm, holder = CharmmPsfFile._parse_psf_section(psf, int)
else:
ncrterm = pointer
# At this point, ncrterm and holder are both set to the CMAP list for
......@@ -370,7 +370,7 @@ class CharmmPsfFile(object):
- data (list) : A list of all data in the parsed section converted
to `dtype'
"""
conv = ProteinStructure._convert
conv = CharmmPsfFile._convert
words = psf.readline().split()
if len(words) == 1:
pointers = conv(words[0], int, 'pointer')
......@@ -400,7 +400,7 @@ class CharmmPsfFile(object):
- vmd (bool) : If True, it will write out a PSF in the format that
VMD prints it in (i.e., no NUMLP/NUMLPH or MOLNT sections)
Example:
>>> cs = ProteinStructure.load_from_psf('testfiles/test.psf')
>>> cs = CharmmPsfFile.load_from_psf('testfiles/test.psf')
>>> cs.write_psf('testfiles/test2.psf')
"""
# See if this is an extended format
......@@ -769,8 +769,13 @@ class CharmmPsfFile(object):
if atom.residue.idx != last_residue:
last_residue = atom.residue.idx
residue = topology.addResidue(atom.residue.resname, chain)
atomic_num = atom.type.atomic_number
elem = element.Element.getByAtomicNumber(atomic_num)
if atom.type is not None:
# This is the most reliable way of determining the element
atomic_num = atom.type.atomic_number
elem = element.Element.getByAtomicNumber(atomic_num)
else:
# If we didn't load a parameter set yet, try based on mass
elem = element.Element.getByMass(atom.mass)
topology.addAtom(atom.name, elem, residue)
# Add all of the bonds
......@@ -933,7 +938,7 @@ class CharmmPsfFile(object):
# back up the dihedral list
dihedral_list = self.dihedral_list
# Load the parameter set
self.load_parameters(params)
self.load_parameters(params.condense())
hasbox = self.topology.getUnitCellDimensions() is not None
# Set the cutoff distance in nanometers
cutoff = None
......
......@@ -34,7 +34,7 @@ __author__ = "Christopher M. Bruns"
__version__ = "1.0"
from simtk.unit import daltons
from simtk.unit import daltons, is_quantity
import copy_reg
class Element(object):
......@@ -93,6 +93,23 @@ class Element(object):
def getByAtomicNumber(atomic_number):
return Element._elements_by_atomic_number[atomic_number]
@staticmethod
def getByMass(mass):
# Assume masses are in daltons if they are not units
if not is_quantity(mass):
mass = mass * daltons
diff = mass
best_guess = 'EP'
for key in Element._elements_by_atomic_number:
element = Element._elements_by_atomic_number[key]
massdiff = abs(element.mass - mass)
if massdiff < diff:
best_guess = element
diff = massdiff
return best_guess
@property
def atomic_number(self):
return self._atomic_number
......
......@@ -13,20 +13,13 @@ class TestCharmmFiles(unittest.TestCase):
"""Set up the tests by loading the input files."""
# alanine tripeptide; no waters
self.psf_c = CharmmPSF.load_from_psf('systems/ala_ala_ala.psf')
self.psf_x = CharmmPSF.load_from_psf('systems/ala_ala_ala.xpsf')
self.psf_v = CharmmPSF.load_from_psf('systems/ala_ala_ala.vpsf')
params = CharmmParameterSet.load_set(
tfile='systems/charmm22.rtf',
pfile='systems/charmm22.par',
).condense()
self.psf_c = CharmmPsfFile('systems/ala_ala_ala.psf')
self.psf_x = CharmmPsfFile('systems/ala_ala_ala.xpsf')
self.psf_v = CharmmPsfFile('systems/ala_ala_ala.vpsf')
self.params = CharmmParameterSet(
'systems/charmm22.rtf', 'systems/charmm22.par')
self.pdb = PDBFile('systems/ala_ala_ala.pdb')
# Load parameters
self.psf_c.load_parameters(params)
self.psf_x.load_parameters(params)
self.psf_v.load_parameters(params)
def test_NonbondedMethod(self):
"""Test both non-periodic methods for the systems"""
......@@ -34,7 +27,7 @@ class TestCharmmFiles(unittest.TestCase):
CutoffNonPeriodic:NonbondedForce.CutoffNonPeriodic}
for top in (self.psf_c, self.psf_x, self.psf_v):
for method in methodMap:
system = top.createSystem(nonbondedMethod=method)
system = top.createSystem(self.params, nonbondedMethod=method)
forces = system.getForces()
self.assertTrue(any(isinstance(f, NonbondedForce) and
f.getNonbondedMethod()==methodMap[method]
......@@ -45,7 +38,7 @@ class TestCharmmFiles(unittest.TestCase):
for top in (self.psf_c, self.psf_x, self.psf_v):
for method in [CutoffNonPeriodic]:
system = top.createSystem(nonbondedMethod=method,
system = top.createSystem(self.params, nonbondedMethod=method,
nonbondedCutoff=2*nanometer,
constraints=HBonds)
cutoff_distance = 0.0*nanometer
......@@ -59,21 +52,21 @@ class TestCharmmFiles(unittest.TestCase):
"""Test both options (True and False) for the removeCMMotion parameter."""
for b in [True, False]:
system = self.psf_c.createSystem(removeCMMotion=b)
system = self.psf_c.createSystem(self.params, removeCMMotion=b)
self.assertEqual(any(isinstance(f, CMMotionRemover) for f in system.getForces()), b)
def test_ImplicitSolvent(self):
"""Test implicit solvent using the implicitSolvent parameter.
"""
system = self.psf_v.createSystem(implicitSolvent=OBC2)
system = self.psf_v.createSystem(self.params, implicitSolvent=OBC2)
self.assertTrue(any(isinstance(f, CustomGBForce) for f in system.getForces()))
def test_ImplicitSolventParameters(self):
"""Test that solventDielectric and soluteDielectric are passed correctly.
"""
system = self.psf_x.createSystem(implicitSolvent=GBn,
system = self.psf_x.createSystem(self.params, implicitSolvent=GBn,
solventDielectric=50.0,
soluteDielectric = 0.9)
found_matching_solvent_dielectric=False
......@@ -97,8 +90,8 @@ class TestCharmmFiles(unittest.TestCase):
topology = self.psf_v.topology
hydrogenMass = 4*amu
system1 = self.psf_v.createSystem()
system2 = self.psf_v.createSystem(hydrogenMass=hydrogenMass)
system1 = self.psf_v.createSystem(self.params)
system2 = self.psf_v.createSystem(self.params, hydrogenMass=hydrogenMass)
for atom in topology.atoms():
if atom.element == elem.hydrogen:
self.assertNotEqual(hydrogenMass, system1.getParticleMass(atom.index))
......
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