Commit 71d33617 authored by John Chodera (MSKCC)'s avatar John Chodera (MSKCC)
Browse files

Merge remote-tracking branch 'upstream/master'

parents eb232608 9da36463
...@@ -48,10 +48,11 @@ ...@@ -48,10 +48,11 @@
using namespace OpenMM; using namespace OpenMM;
using namespace std; using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5; const double TOL = 1e-5;
void testSingleBond() { void testSingleBond() {
ReferencePlatform platform;
System system; System system;
system.addParticle(2.0); system.addParticle(2.0);
system.addParticle(2.0); system.addParticle(2.0);
...@@ -89,7 +90,6 @@ void testSingleBond() { ...@@ -89,7 +90,6 @@ void testSingleBond() {
void testConstraints() { void testConstraints() {
const int numParticles = 8; const int numParticles = 8;
const double temp = 500.0; const double temp = 500.0;
ReferencePlatform platform;
System system; System system;
VariableVerletIntegrator integrator(1e-5); VariableVerletIntegrator integrator(1e-5);
integrator.setConstraintTolerance(1e-5); integrator.setConstraintTolerance(1e-5);
...@@ -148,7 +148,6 @@ void testConstraints() { ...@@ -148,7 +148,6 @@ void testConstraints() {
void testConstrainedClusters() { void testConstrainedClusters() {
const int numParticles = 7; const int numParticles = 7;
const double temp = 500.0; const double temp = 500.0;
ReferencePlatform platform;
System system; System system;
VariableVerletIntegrator integrator(1e-5); VariableVerletIntegrator integrator(1e-5);
integrator.setConstraintTolerance(1e-5); integrator.setConstraintTolerance(1e-5);
...@@ -211,7 +210,6 @@ void testConstrainedClusters() { ...@@ -211,7 +210,6 @@ void testConstrainedClusters() {
} }
void testConstrainedMasslessParticles() { void testConstrainedMasslessParticles() {
ReferencePlatform platform;
System system; System system;
system.addParticle(0.0); system.addParticle(0.0);
system.addParticle(1.0); system.addParticle(1.0);
...@@ -255,7 +253,6 @@ void testArgonBox() { ...@@ -255,7 +253,6 @@ void testArgonBox() {
// Create a box of argon atoms. // Create a box of argon atoms.
ReferencePlatform platform;
System system; System system;
NonbondedForce* nonbonded = new NonbondedForce(); NonbondedForce* nonbonded = new NonbondedForce();
vector<Vec3> positions; vector<Vec3> positions;
......
...@@ -48,10 +48,11 @@ ...@@ -48,10 +48,11 @@
using namespace OpenMM; using namespace OpenMM;
using namespace std; using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5; const double TOL = 1e-5;
void testSingleBond() { void testSingleBond() {
ReferencePlatform platform;
System system; System system;
system.addParticle(2.0); system.addParticle(2.0);
system.addParticle(2.0); system.addParticle(2.0);
...@@ -88,7 +89,6 @@ void testSingleBond() { ...@@ -88,7 +89,6 @@ void testSingleBond() {
void testConstraints() { void testConstraints() {
const int numParticles = 8; const int numParticles = 8;
const double temp = 500.0; const double temp = 500.0;
ReferencePlatform platform;
System system; System system;
VerletIntegrator integrator(0.002); VerletIntegrator integrator(0.002);
integrator.setConstraintTolerance(1e-5); integrator.setConstraintTolerance(1e-5);
...@@ -139,7 +139,6 @@ void testConstraints() { ...@@ -139,7 +139,6 @@ void testConstraints() {
void testConstrainedClusters() { void testConstrainedClusters() {
const int numParticles = 7; const int numParticles = 7;
const double temp = 500.0; const double temp = 500.0;
ReferencePlatform platform;
System system; System system;
VerletIntegrator integrator(0.001); VerletIntegrator integrator(0.001);
integrator.setConstraintTolerance(1e-5); integrator.setConstraintTolerance(1e-5);
...@@ -201,7 +200,6 @@ void testConstrainedClusters() { ...@@ -201,7 +200,6 @@ void testConstrainedClusters() {
} }
void testConstrainedMasslessParticles() { void testConstrainedMasslessParticles() {
ReferencePlatform platform;
System system; System system;
system.addParticle(0.0); system.addParticle(0.0);
system.addParticle(1.0); system.addParticle(1.0);
......
...@@ -50,11 +50,12 @@ ...@@ -50,11 +50,12 @@
using namespace OpenMM; using namespace OpenMM;
using namespace std; using namespace std;
ReferencePlatform platform;
/** /**
* Check that massless particles are handled correctly. * Check that massless particles are handled correctly.
*/ */
void testMasslessParticle() { void testMasslessParticle() {
ReferencePlatform platform;
System system; System system;
system.addParticle(0.0); system.addParticle(0.0);
system.addParticle(1.0); system.addParticle(1.0);
...@@ -91,7 +92,6 @@ void testMasslessParticle() { ...@@ -91,7 +92,6 @@ void testMasslessParticle() {
* Test a TwoParticleAverageSite virtual site. * Test a TwoParticleAverageSite virtual site.
*/ */
void testTwoParticleAverage() { void testTwoParticleAverage() {
ReferencePlatform platform;
System system; System system;
system.addParticle(1.0); system.addParticle(1.0);
system.addParticle(1.0); system.addParticle(1.0);
...@@ -128,7 +128,6 @@ void testTwoParticleAverage() { ...@@ -128,7 +128,6 @@ void testTwoParticleAverage() {
* Test a ThreeParticleAverageSite virtual site. * Test a ThreeParticleAverageSite virtual site.
*/ */
void testThreeParticleAverage() { void testThreeParticleAverage() {
ReferencePlatform platform;
System system; System system;
system.addParticle(1.0); system.addParticle(1.0);
system.addParticle(1.0); system.addParticle(1.0);
...@@ -170,7 +169,6 @@ void testThreeParticleAverage() { ...@@ -170,7 +169,6 @@ void testThreeParticleAverage() {
* Test an OutOfPlaneSite virtual site. * Test an OutOfPlaneSite virtual site.
*/ */
void testOutOfPlane() { void testOutOfPlane() {
ReferencePlatform platform;
System system; System system;
system.addParticle(1.0); system.addParticle(1.0);
system.addParticle(1.0); system.addParticle(1.0);
...@@ -223,7 +221,6 @@ void testLocalCoordinates() { ...@@ -223,7 +221,6 @@ void testLocalCoordinates() {
const Vec3 xWeights(-1.0, 0.5, 0.5); const Vec3 xWeights(-1.0, 0.5, 0.5);
const Vec3 yWeights(0.0, -1.0, 1.0); const Vec3 yWeights(0.0, -1.0, 1.0);
const Vec3 localPosition(0.4, 0.3, 0.2); const Vec3 localPosition(0.4, 0.3, 0.2);
ReferencePlatform platform;
System system; System system;
system.addParticle(1.0); system.addParticle(1.0);
system.addParticle(1.0); system.addParticle(1.0);
...@@ -299,7 +296,6 @@ void testLocalCoordinates() { ...@@ -299,7 +296,6 @@ void testLocalCoordinates() {
* when using virtual sites. * when using virtual sites.
*/ */
void testConservationLaws() { void testConservationLaws() {
ReferencePlatform platform;
System system; System system;
NonbondedForce* forceField = new NonbondedForce(); NonbondedForce* forceField = new NonbondedForce();
system.addForce(forceField); system.addForce(forceField);
......
...@@ -159,7 +159,7 @@ public: ...@@ -159,7 +159,7 @@ public:
/** /**
* Update the per-angle parameters in a Context to match those stored in this Force object. This method provides * Update the per-angle parameters in a Context to match those stored in this Force object. This method provides
* an efficient method to update certain parameters in an existing Context without needing to reinitialize it. * an efficient method to update certain parameters in an existing Context without needing to reinitialize it.
* Simply call setAngleParameters() to modify this object's parameters, then call updateParametersInState() * Simply call setAngleParameters() to modify this object's parameters, then call updateParametersInContext()
* to copy them over to the Context. * to copy them over to the Context.
* *
* The only information this method updates is the values of per-angle parameters. The set of particles involved * The only information this method updates is the values of per-angle parameters. The set of particles involved
......
...@@ -131,7 +131,7 @@ public: ...@@ -131,7 +131,7 @@ public:
/** /**
* Update the per-bond parameters in a Context to match those stored in this Force object. This method provides * Update the per-bond parameters in a Context to match those stored in this Force object. This method provides
* an efficient method to update certain parameters in an existing Context without needing to reinitialize it. * an efficient method to update certain parameters in an existing Context without needing to reinitialize it.
* Simply call setBondParameters() to modify this object's parameters, then call updateParametersInState() * Simply call setBondParameters() to modify this object's parameters, then call updateParametersInContext()
* to copy them over to the Context. * to copy them over to the Context.
* *
* The only information this method updates is the values of per-bond parameters. The set of particles involved * The only information this method updates is the values of per-bond parameters. The set of particles involved
......
...@@ -156,7 +156,7 @@ public: ...@@ -156,7 +156,7 @@ public:
/** /**
* Update the per-particle parameters in a Context to match those stored in this Force object. This method provides * Update the per-particle parameters in a Context to match those stored in this Force object. This method provides
* an efficient method to update certain parameters in an existing Context without needing to reinitialize it. * an efficient method to update certain parameters in an existing Context without needing to reinitialize it.
* Simply call setParticleParameters() to modify this object's parameters, then call updateParametersInState() * Simply call setParticleParameters() to modify this object's parameters, then call updateParametersInContext()
* to copy them over to the Context. * to copy them over to the Context.
* *
* The only information this method updates is the values of per-particle parameters. All other aspects of the Force * The only information this method updates is the values of per-particle parameters. All other aspects of the Force
......
...@@ -164,7 +164,7 @@ public: ...@@ -164,7 +164,7 @@ public:
/** /**
* Update the per-angle parameters in a Context to match those stored in this Force object. This method provides * Update the per-angle parameters in a Context to match those stored in this Force object. This method provides
* an efficient method to update certain parameters in an existing Context without needing to reinitialize it. * an efficient method to update certain parameters in an existing Context without needing to reinitialize it.
* Simply call setAngleParameters() to modify this object's parameters, then call updateParametersInState() * Simply call setAngleParameters() to modify this object's parameters, then call updateParametersInContext()
* to copy them over to the Context. * to copy them over to the Context.
* *
* The only information this method updates is the values of per-angle parameters. The set of particles involved * The only information this method updates is the values of per-angle parameters. The set of particles involved
......
...@@ -340,7 +340,7 @@ public: ...@@ -340,7 +340,7 @@ public:
/** /**
* Update the multipole parameters in a Context to match those stored in this Force object. This method * Update the multipole parameters in a Context to match those stored in this Force object. This method
* provides an efficient method to update certain parameters in an existing Context without needing to reinitialize it. * provides an efficient method to update certain parameters in an existing Context without needing to reinitialize it.
* Simply call setMultipoleParameters() to modify this object's parameters, then call updateParametersInState() to * Simply call setMultipoleParameters() to modify this object's parameters, then call updateParametersInContext() to
* copy them over to the Context. * copy them over to the Context.
* *
* This method has several limitations. The only information it updates is the parameters of multipoles. * This method has several limitations. The only information it updates is the parameters of multipoles.
......
...@@ -156,7 +156,7 @@ public: ...@@ -156,7 +156,7 @@ public:
/** /**
* Update the per-bend term parameters in a Context to match those stored in this Force object. This method provides * Update the per-bend term parameters in a Context to match those stored in this Force object. This method provides
* an efficient method to update certain parameters in an existing Context without needing to reinitialize it. * an efficient method to update certain parameters in an existing Context without needing to reinitialize it.
* Simply call setOutOfPlaneBendParameters() to modify this object's parameters, then call updateParametersInState() * Simply call setOutOfPlaneBendParameters() to modify this object's parameters, then call updateParametersInContext()
* to copy them over to the Context. * to copy them over to the Context.
* *
* The only information this method updates is the values of per-bend term parameters. The set of particles involved * The only information this method updates is the values of per-bend term parameters. The set of particles involved
......
...@@ -106,7 +106,7 @@ public: ...@@ -106,7 +106,7 @@ public:
/** /**
* Update the per-torsion parameters in a Context to match those stored in this Force object. This method provides * Update the per-torsion parameters in a Context to match those stored in this Force object. This method provides
* an efficient method to update certain parameters in an existing Context without needing to reinitialize it. * an efficient method to update certain parameters in an existing Context without needing to reinitialize it.
* Simply call setPiTorsionParameters() to modify this object's parameters, then call updateParametersInState() * Simply call setPiTorsionParameters() to modify this object's parameters, then call updateParametersInContext()
* to copy them over to the Context. * to copy them over to the Context.
* *
* The only information this method updates is the values of per-torsion parameters. The set of particles involved * The only information this method updates is the values of per-torsion parameters. The set of particles involved
......
...@@ -112,7 +112,7 @@ public: ...@@ -112,7 +112,7 @@ public:
/** /**
* Update the per-stretch-bend term parameters in a Context to match those stored in this Force object. This method provides * Update the per-stretch-bend term parameters in a Context to match those stored in this Force object. This method provides
* an efficient method to update certain parameters in an existing Context without needing to reinitialize it. * an efficient method to update certain parameters in an existing Context without needing to reinitialize it.
* Simply call setStretchBendParameters() to modify this object's parameters, then call updateParametersInState() * Simply call setStretchBendParameters() to modify this object's parameters, then call updateParametersInContext()
* to copy them over to the Context. * to copy them over to the Context.
* *
* The only information this method updates is the values of per-stretch-bend term parameters. The set of particles involved * The only information this method updates is the values of per-stretch-bend term parameters. The set of particles involved
......
...@@ -205,7 +205,7 @@ public: ...@@ -205,7 +205,7 @@ public:
/** /**
* Update the per-particle parameters in a Context to match those stored in this Force object. This method provides * Update the per-particle parameters in a Context to match those stored in this Force object. This method provides
* an efficient method to update certain parameters in an existing Context without needing to reinitialize it. * an efficient method to update certain parameters in an existing Context without needing to reinitialize it.
* Simply call setParticleParameters() to modify this object's parameters, then call updateParametersInState() * Simply call setParticleParameters() to modify this object's parameters, then call updateParametersInContext()
* to copy them over to the Context. * to copy them over to the Context.
* *
* The only information this method updates is the values of per-particle parameters. All other aspects of the Force * The only information this method updates is the values of per-particle parameters. All other aspects of the Force
......
...@@ -92,7 +92,7 @@ public: ...@@ -92,7 +92,7 @@ public:
/** /**
* Update the per-particle parameters in a Context to match those stored in this Force object. This method provides * Update the per-particle parameters in a Context to match those stored in this Force object. This method provides
* an efficient method to update certain parameters in an existing Context without needing to reinitialize it. * an efficient method to update certain parameters in an existing Context without needing to reinitialize it.
* Simply call setParticleParameters() to modify this object's parameters, then call updateParametersInState() * Simply call setParticleParameters() to modify this object's parameters, then call updateParametersInContext()
* to copy them over to the Context. * to copy them over to the Context.
* *
* The only information this method updates is the values of per-particle parameters. All other aspects of the Force * The only information this method updates is the values of per-particle parameters. All other aspects of the Force
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* 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) 2014 Stanford University and the Authors. * * Portions copyright (c) 2014-2015 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -148,6 +148,7 @@ void testMathFunctions() { ...@@ -148,6 +148,7 @@ void testMathFunctions() {
ASSERT_VEC4_EQUAL(min(f1, f2), 0.4, 1.2, -1.2, -5.0); ASSERT_VEC4_EQUAL(min(f1, f2), 0.4, 1.2, -1.2, -5.0);
ASSERT_VEC4_EQUAL(max(f1, f2), 1.1, 1.9, 1.3, -3.8); ASSERT_VEC4_EQUAL(max(f1, f2), 1.1, 1.9, 1.3, -3.8);
ASSERT_VEC4_EQUAL(sqrt(fvec4(1.5, 3.1, 4.0, 15.0)), sqrt(1.5), sqrt(3.1), sqrt(4.0), sqrt(15.0)); ASSERT_VEC4_EQUAL(sqrt(fvec4(1.5, 3.1, 4.0, 15.0)), sqrt(1.5), sqrt(3.1), sqrt(4.0), sqrt(15.0));
ASSERT_VEC4_EQUAL(rsqrt(fvec4(1.5, 3.1, 4.0, 15.0)), 1.0/sqrt(1.5), 1.0/sqrt(3.1), 1.0/sqrt(4.0), 1.0/sqrt(15.0));
ASSERT_EQUAL_TOL(f1[0]*f2[0]+f1[1]*f2[1]+f1[2]*f2[2], dot3(f1, f2), 1e-6); ASSERT_EQUAL_TOL(f1[0]*f2[0]+f1[1]*f2[1]+f1[2]*f2[2], dot3(f1, f2), 1e-6);
ASSERT_EQUAL_TOL(f1[0]*f2[0]+f1[1]*f2[1]+f1[2]*f2[2]+f1[3]*f2[3], dot4(f1, f2), 1e-6); ASSERT_EQUAL_TOL(f1[0]*f2[0]+f1[1]*f2[1]+f1[2]*f2[2]+f1[3]*f2[3], dot4(f1, f2), 1e-6);
ASSERT(any(f1 > 0.5)); ASSERT(any(f1 > 0.5));
......
...@@ -73,9 +73,8 @@ class AmberPrmtopFile(object): ...@@ -73,9 +73,8 @@ class AmberPrmtopFile(object):
def __init__(self, file): def __init__(self, file):
"""Load a prmtop file.""" """Load a prmtop file."""
top = Topology()
## The Topology read from the prmtop file ## The Topology read from the prmtop file
self.topology = top self.topology = top = Topology()
self.elements = [] self.elements = []
# Load the prmtop file # Load the prmtop file
...@@ -229,7 +228,7 @@ class AmberPrmtopFile(object): ...@@ -229,7 +228,7 @@ class AmberPrmtopFile(object):
elif implicitSolvent is None: elif implicitSolvent is None:
implicitSolventKappa = 0.0 implicitSolventKappa = 0.0
sys = amber_file_parser.readAmberSystem(prmtop_loader=self._prmtop, shake=constraintString, sys = amber_file_parser.readAmberSystem(self.topology, prmtop_loader=self._prmtop, shake=constraintString,
nonbondedCutoff=nonbondedCutoff, nonbondedMethod=methodMap[nonbondedMethod], nonbondedCutoff=nonbondedCutoff, nonbondedMethod=methodMap[nonbondedMethod],
flexibleConstraints=False, gbmodel=implicitString, soluteDielectric=soluteDielectric, flexibleConstraints=False, gbmodel=implicitString, soluteDielectric=soluteDielectric,
solventDielectric=solventDielectric, implicitSolventKappa=implicitSolventKappa, solventDielectric=solventDielectric, implicitSolventKappa=implicitSolventKappa,
......
...@@ -37,6 +37,7 @@ from __future__ import division ...@@ -37,6 +37,7 @@ from __future__ import division
from functools import wraps from functools import wraps
from math import pi, cos, sin, sqrt from math import pi, cos, sin, sqrt
import os import os
import re
import simtk.openmm as mm import simtk.openmm as mm
from simtk.openmm.vec3 import Vec3 from simtk.openmm.vec3 import Vec3
import simtk.unit as u import simtk.unit as u
...@@ -99,6 +100,8 @@ class _ZeroDict(dict): ...@@ -99,6 +100,8 @@ class _ZeroDict(dict):
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
_resre = re.compile(r'(\d+)([a-zA-Z]*)')
class CharmmPsfFile(object): class CharmmPsfFile(object):
""" """
A chemical structure instantiated from CHARMM files. A chemical structure instantiated from CHARMM files.
...@@ -192,11 +195,13 @@ class CharmmPsfFile(object): ...@@ -192,11 +195,13 @@ class CharmmPsfFile(object):
atom_list = AtomList() atom_list = AtomList()
for i in xrange(natom): for i in xrange(natom):
words = psfsections['NATOM'][1][i].split() words = psfsections['NATOM'][1][i].split()
atid = int(words[0])
if atid != i + 1:
raise CharmmPSFError('Nonsequential atoms detected!')
system = words[1] system = words[1]
resid = conv(words[2], int, 'residue number') rematch = _resre.match(words[2])
if not rematch:
raise RuntimeError('Could not parse residue number %s' %
words[2])
resid, inscode = rematch.groups()
resid = int(resid)
resname = words[3] resname = words[3]
name = words[4] name = words[4]
attype = words[5] attype = words[5]
...@@ -209,7 +214,7 @@ class CharmmPsfFile(object): ...@@ -209,7 +214,7 @@ class CharmmPsfFile(object):
mass = conv(words[7], float, 'atomic mass') mass = conv(words[7], float, 'atomic mass')
props = words[8:] props = words[8:]
atom = residue_list.add_atom(system, resid, resname, name, atom = residue_list.add_atom(system, resid, resname, name,
attype, charge, mass, props) attype, charge, mass, inscode, props)
atom_list.append(atom) atom_list.append(atom)
atom_list.assign_indexes() atom_list.assign_indexes()
atom_list.changed = False atom_list.changed = False
...@@ -443,175 +448,6 @@ class CharmmPsfFile(object): ...@@ -443,175 +448,6 @@ class CharmmPsfFile(object):
line = psf.readline().strip() line = psf.readline().strip()
return title, pointers, data return title, pointers, data
def writePsf(self, dest, vmd=False):
"""
Writes a PSF file from the stored molecule
Parameters:
- dest (str or file-like) : The place to write the output PSF file.
If it has a "write" attribute, it will be used to print the
PSF file. Otherwise, it will be treated like a string and a
file will be opened, printed, then closed
- 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 = CharmmPsfFile('testfiles/test.psf')
>>> cs.writePsf('testfiles/test2.psf')
"""
# See if this is an extended format
ext = 'EXT' in self.flags
own_handle = False
# Index the atoms and residues
self.residue_list.assign_indexes()
self.atom_list.assign_indexes()
if not hasattr(dest, 'write'):
own_handle = True
dest = open(dest, 'w')
# Assign the formats we need to write with
if ext:
atmfmt1 = ('%10d %-8s %-8i %-8s %-8s %4d %10.6f %13.4f' + 11*' ')
atmfmt2 = ('%10d %-8s %-8i %-8s %-8s %-4s %10.6f %13.4f' + 11*' ')
intfmt = '%10d' # For pointers
else:
atmfmt1 = ('%8d %-4s %-4i %-4s %-4s %4d %10.6f %13.4f' + 11*' ')
atmfmt2 = ('%8d %-4s %-4i %-4s %-4s %-4s %10.6f %13.4f' + 11*' ')
intfmt = '%8d' # For pointers
# Now print the header then the title
dest.write('PSF ' + ' '.join(self.flags) + '\n')
dest.write('\n')
dest.write(intfmt % len(self.title) + ' !NTITLE\n')
dest.write('\n'.join(self.title) + '\n\n')
# Now time for the atoms
dest.write(intfmt % len(self.atom_list) + ' !NATOM\n')
# atmfmt1 is for CHARMM format (i.e., atom types are integers)
# atmfmt is for XPLOR format (i.e., atom types are strings)
for i, atom in enumerate(self.atom_list):
if isinstance(atom.attype, str):
fmt = atmfmt2
else:
fmt = atmfmt1
atmstr = fmt % (i+1, atom.system, atom.residue.resnum,
atom.residue.resname, atom.name, atom.attype,
atom.charge, atom.mass)
dest.write(atmstr + ' '.join(atom.props) + '\n')
dest.write('\n')
# Bonds
dest.write(intfmt % len(self.bond_list) + ' !NBOND: bonds\n')
for i, bond in enumerate(self.bond_list):
dest.write((intfmt*2) % (bond.atom1.idx+1, bond.atom2.idx+1))
if i % 4 == 3: # Write 4 bonds per line
dest.write('\n')
# See if we need to terminate
if len(self.bond_list) % 4 != 0 or len(self.bond_list) == 0:
dest.write('\n')
dest.write('\n')
# Angles
dest.write(intfmt % len(self.angle_list) + ' !NTHETA: angles\n')
for i, angle in enumerate(self.angle_list):
dest.write((intfmt*3) % (angle.atom1.idx+1, angle.atom2.idx+1,
angle.atom3.idx+1)
)
if i % 3 == 2: # Write 3 angles per line
dest.write('\n')
# See if we need to terminate
if len(self.angle_list) % 3 != 0 or len(self.angle_list) == 0:
dest.write('\n')
dest.write('\n')
# Dihedrals
dest.write(intfmt % len(self.dihedral_list) + ' !NPHI: dihedrals\n')
for i, dih in enumerate(self.dihedral_list):
dest.write((intfmt*4) % (dih.atom1.idx+1, dih.atom2.idx+1,
dih.atom3.idx+1, dih.atom4.idx+1)
)
if i % 2 == 1: # Write 2 dihedrals per line
dest.write('\n')
# See if we need to terminate
if len(self.dihedral_list) % 2 != 0 or len(self.dihedral_list) == 0:
dest.write('\n')
dest.write('\n')
# Impropers
dest.write(intfmt % len(self.improper_list) + ' !NIMPHI: impropers\n')
for i, imp in enumerate(self.improper_list):
dest.write((intfmt*4) % (imp.atom1.idx+1, imp.atom2.idx+1,
imp.atom3.idx+1, imp.atom4.idx+1)
)
if i % 2 == 1: # Write 2 dihedrals per line
dest.write('\n')
# See if we need to terminate
if len(self.improper_list) % 2 != 0 or len(self.improper_list) == 0:
dest.write('\n')
dest.write('\n')
# Donor section
dest.write(intfmt % len(self.donor_list) + ' !NDON: donors\n')
for i, don in enumerate(self.donor_list):
dest.write((intfmt*2) % (don.atom1.idx+1, don.atom2.idx+1))
if i % 4 == 3: # 4 donors per line
dest.write('\n')
if len(self.donor_list) % 4 != 0 or len(self.donor_list) == 0:
dest.write('\n')
dest.write('\n')
# Acceptor section
dest.write(intfmt % len(self.acceptor_list) + ' !NACC: acceptors\n')
for i, acc in enumerate(self.acceptor_list):
dest.write((intfmt*2) % (acc.atom1.idx+1, acc.atom2.idx+1))
if i % 4 == 3: # 4 donors per line
dest.write('\n')
if len(self.acceptor_list) % 4 != 0 or len(self.acceptor_list) == 0:
dest.write('\n')
dest.write('\n')
# NNB section ??
dest.write(intfmt % 0 + ' !NNB\n\n')
for i in range(len(self.atom_list)):
dest.write(intfmt % 0)
if i % 8 == 7: # Write 8 0's per line
dest.write('\n')
if len(self.atom_list) % 8 != 0: dest.write('\n')
dest.write('\n')
# Group section
dest.write((intfmt*2) % (len(self.group_list), self.group_list.nst2))
dest.write(' !NGRP NST2\n')
for i, gp in enumerate(self.group_list):
dest.write((intfmt*3) % (gp.bs, gp.type, gp.move))
if i % 3 == 2: dest.write('\n')
if len(self.group_list) % 3 != 0 or len(self.group_list) == 0:
dest.write('\n')
dest.write('\n')
# The next two sections are never found in VMD prmtops...
if not vmd:
# Molecule section; first set molecularity
set_molecules(self.atom_list)
mollist = [a.marked for a in self.atom_list]
dest.write(intfmt % max(mollist) + ' !MOLNT\n')
for i, atom in enumerate(self.atom_list):
dest.write(intfmt % atom.marked)
if i % 8 == 7: dest.write('\n')
if len(self.atom_list) % 8 != 0: dest.write('\n')
dest.write('\n')
# NUMLP/NUMLPH section
dest.write((intfmt*2) % (0, 0) + ' !NUMLP NUMLPH\n')
dest.write('\n')
# CMAP section
dest.write(intfmt % len(self.cmap_list) + ' !NCRTERM: cross-terms\n')
for i, cmap in enumerate(self.cmap_list):
dest.write((intfmt*4) % (cmap.atom1.idx+1, cmap.atom2.idx+1,
cmap.atom3.idx+1, cmap.atom4.idx+1)
)
if cmap.consecutive:
dest.write((intfmt*4) % (cmap.atom2.idx+1, cmap.atom3.idx+1,
cmap.atom4.idx+1, cmap.atom5.idx+1)
)
else:
dest.write((intfmt*4) % (cmap.atom5.idx+1, cmap.atom6.idx+1,
cmap.atom7.idx+1, cmap.atom8.idx+1)
)
dest.write('\n')
# Done!
# If we opened our own handle, close it
if own_handle:
dest.close()
def loadParameters(self, parmset): def loadParameters(self, parmset):
""" """
Loads parameters from a parameter set that was loaded via CHARMM RTF, Loads parameters from a parameter set that was loaded via CHARMM RTF,
...@@ -776,13 +612,13 @@ class CharmmPsfFile(object): ...@@ -776,13 +612,13 @@ class CharmmPsfFile(object):
last_residue = None last_residue = None
# Add each chain (separate 'system's) and residue # Add each chain (separate 'system's) and residue
for atom in self.atom_list: for atom in self.atom_list:
resid = '%d%s' % (atom.residue.idx, atom.residue.inscode)
if atom.system != last_chain: if atom.system != last_chain:
chain = topology.addChain(atom.system) chain = topology.addChain(atom.system)
last_chain = atom.system last_chain = atom.system
if atom.residue.idx != last_residue: if resid != last_residue:
last_residue = atom.residue.idx last_residue = resid
residue = topology.addResidue(atom.residue.resname, chain, residue = topology.addResidue(atom.residue.resname, chain, resid)
str(atom.residue.idx))
if atom.type is not None: if atom.type is not None:
# This is the most reliable way of determining the element # This is the most reliable way of determining the element
atomic_num = atom.type.atomic_number atomic_num = atom.type.atomic_number
......
...@@ -37,6 +37,8 @@ import time ...@@ -37,6 +37,8 @@ import time
import struct import struct
import math import math
from simtk.unit import picoseconds, nanometers, angstroms, is_quantity, norm from simtk.unit import picoseconds, nanometers, angstroms, is_quantity, norm
from simtk.openmm import Vec3
from simtk.openmm.app.internal.unitcell import computeLengthsAndAngles
class DCDFile(object): class DCDFile(object):
"""DCDFile provides methods for creating DCD files. """DCDFile provides methods for creating DCD files.
...@@ -112,16 +114,16 @@ class DCDFile(object): ...@@ -112,16 +114,16 @@ class DCDFile(object):
file.seek(0, os.SEEK_END) file.seek(0, os.SEEK_END)
boxVectors = self._topology.getPeriodicBoxVectors() boxVectors = self._topology.getPeriodicBoxVectors()
if boxVectors is not None: if boxVectors is not None:
if getPeriodicBoxVectors is not None: if periodicBoxVectors is not None:
boxVectors = getPeriodicBoxVectors boxVectors = periodicBoxVectors
elif unitCellDimensions is not None: elif unitCellDimensions is not None:
if is_quantity(unitCellDimensions): if is_quantity(unitCellDimensions):
unitCellDimensions = unitCellDimensions.value_in_unit(nanometers) unitCellDimensions = unitCellDimensions.value_in_unit(nanometers)
boxVectors = (Vec3(unitCellDimensions[0], 0, 0), Vec3(0, unitCellDimensions[1], 0), Vec3(0, 0, unitCellDimensions[2]))*nanometers boxVectors = (Vec3(unitCellDimensions[0], 0, 0), Vec3(0, unitCellDimensions[1], 0), Vec3(0, 0, unitCellDimensions[2]))*nanometers
(a_length, b_length, c_length, alpha, beta, gamma) = computeLengthsAndAngles(boxVectors) (a_length, b_length, c_length, alpha, beta, gamma) = computeLengthsAndAngles(boxVectors)
a_length = a_length.value_in_unit(angstroms) a_length = a_length * 10. # computeLengthsAndAngles returns unitless nanometers, but need angstroms here.
b_length = b_length.value_in_unit(angstroms) b_length = b_length * 10. # computeLengthsAndAngles returns unitless nanometers, but need angstroms here.
c_length = c_length.value_in_unit(angstroms) c_length = c_length * 10. # computeLengthsAndAngles returns unitless nanometers, but need angstroms here.
angle1 = math.sin(math.pi/2-gamma) angle1 = math.sin(math.pi/2-gamma)
angle2 = math.sin(math.pi/2-beta) angle2 = math.sin(math.pi/2-beta)
angle3 = math.sin(math.pi/2-alpha) angle3 = math.sin(math.pi/2-alpha)
......
...@@ -31,7 +31,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -31,7 +31,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
__author__ = "Christopher M. Bruns" __author__ = "Christopher M. Bruns"
__version__ = "1.0" __version__ = "1.0"
from collections import OrderedDict
from simtk.unit import daltons, is_quantity from simtk.unit import daltons, is_quantity
import copy_reg import copy_reg
...@@ -47,6 +47,7 @@ class Element(object): ...@@ -47,6 +47,7 @@ class Element(object):
_elements_by_symbol = {} _elements_by_symbol = {}
_elements_by_atomic_number = {} _elements_by_atomic_number = {}
_elements_by_mass = None
def __init__(self, number, name, symbol, mass): def __init__(self, number, name, symbol, mass):
"""Create a new element """Create a new element
...@@ -67,8 +68,11 @@ class Element(object): ...@@ -67,8 +68,11 @@ class Element(object):
self._mass = mass self._mass = mass
# Index this element in a global table # Index this element in a global table
s = symbol.strip().upper() s = symbol.strip().upper()
## If we add a new element, we need to re-hash elements by mass
Element._elements_by_mass = None
assert s not in Element._elements_by_symbol if s in Element._elements_by_symbol:
raise ValueError('Duplicate element symbol %s' % s)
Element._elements_by_symbol[s] = self Element._elements_by_symbol[s] = self
if number in Element._elements_by_atomic_number: if number in Element._elements_by_atomic_number:
other_element = Element._elements_by_atomic_number[number] other_element = Element._elements_by_atomic_number[number]
...@@ -96,20 +100,45 @@ class Element(object): ...@@ -96,20 +100,45 @@ class Element(object):
""" """
Get the element whose mass is CLOSEST to the requested mass. This method Get the element whose mass is CLOSEST to the requested mass. This method
should not be used for repartitioned masses should not be used for repartitioned masses
Parameters
----------
mass : float or Quantity
Mass of the atom to find the element for. Units assumed to be
daltons if not specified
Returns
-------
element : Element
The element whose atomic mass is closest to the input mass
""" """
# Assume masses are in daltons if they are not units # Assume masses are in daltons if they are not units
if not is_quantity(mass): if is_quantity(mass):
mass = mass * daltons mass = mass.value_in_unit(daltons)
if mass < 0:
raise ValueError('Invalid Higgs field')
# If this is our first time calling getByMass (or we added an element
# since the last call), re-generate the ordered by-mass dict cache
if Element._elements_by_mass is None:
Element._elements_by_mass = OrderedDict()
for elem in sorted(Element._elements_by_symbol.values(),
key=lambda x: x.mass):
Element._elements_by_mass[elem.mass] = elem
diff = mass diff = mass
best_guess = None best_guess = None
for key in Element._elements_by_atomic_number: for elemmass, element in Element._elements_by_mass.iteritems():
element = Element._elements_by_atomic_number[key] massdiff = abs(elemmass._value - mass)
massdiff = abs(element.mass - mass)
if massdiff < diff: if massdiff < diff:
best_guess = element best_guess = element
diff = massdiff diff = massdiff
if elemmass._value > mass:
# Elements are only getting heavier, so bail out early
return best_guess
# This really should only happen if we wanted ununoctium or something
# bigger... won't really happen but still make sure we return an Element
return best_guess return best_guess
@property @property
...@@ -145,6 +174,9 @@ def _pickle_element(element): ...@@ -145,6 +174,9 @@ def _pickle_element(element):
copy_reg.pickle(Element, _pickle_element) copy_reg.pickle(Element, _pickle_element)
# NOTE: getElementByMass assumes all masses are Quantity instances with unit
# "daltons". All elements need to obey this assumption, or that method will
# fail. No checking is done in getElementByMass for performance reasons
hydrogen = Element( 1, "hydrogen", "H", 1.007947*daltons) hydrogen = Element( 1, "hydrogen", "H", 1.007947*daltons)
deuterium = Element( 1, "deuterium", "D", 2.01355321270*daltons) deuterium = Element( 1, "deuterium", "D", 2.01355321270*daltons)
helium = Element( 2, "helium", "He", 4.003*daltons) helium = Element( 2, "helium", "He", 4.003*daltons)
......
...@@ -122,21 +122,22 @@ class PrmtopLoader(object): ...@@ -122,21 +122,22 @@ class PrmtopLoader(object):
with open(inFilename, 'r') as fIn: with open(inFilename, 'r') as fIn:
for line in fIn: for line in fIn:
if line.startswith('%VERSION'): if line[0] == '%':
tag, self._prmtopVersion = line.rstrip().split(None, 1) if line.startswith('%VERSION'):
elif line.startswith('%FLAG'): tag, self._prmtopVersion = line.rstrip().split(None, 1)
tag, flag = line.rstrip().split(None, 1) elif line.startswith('%FLAG'):
self._flags.append(flag) tag, flag = line.rstrip().split(None, 1)
self._raw_data[flag] = [] self._flags.append(flag)
elif line.startswith('%FORMAT'): self._raw_data[flag] = []
format = line.rstrip() elif line.startswith('%FORMAT'):
index0=format.index('(') format = line.rstrip()
index1=format.index(')') index0=format.index('(')
format = format[index0+1:index1] index1=format.index(')')
m = FORMAT_RE_PATTERN.search(format) format = format[index0+1:index1]
self._raw_format[self._flags[-1]] = (format, m.group(1), m.group(2), m.group(3), m.group(4)) m = FORMAT_RE_PATTERN.search(format)
elif line.startswith('%COMMENT'): self._raw_format[self._flags[-1]] = (format, m.group(1), m.group(2), int(m.group(3)), m.group(4))
continue elif line.startswith('%COMMENT'):
continue
elif self._flags \ elif self._flags \
and 'TITLE'==self._flags[-1] \ and 'TITLE'==self._flags[-1] \
and not self._raw_data['TITLE']: and not self._raw_data['TITLE']:
...@@ -144,8 +145,7 @@ class PrmtopLoader(object): ...@@ -144,8 +145,7 @@ class PrmtopLoader(object):
else: else:
flag=self._flags[-1] flag=self._flags[-1]
(format, numItems, itemType, (format, numItems, itemType,
itemLength, itemPrecision) = self._getFormat(flag) iLength, itemPrecision) = self._getFormat(flag)
iLength=int(itemLength)
line = line.rstrip() line = line.rstrip()
for index in range(0, len(line), iLength): for index in range(0, len(line), iLength):
item = line[index:index+iLength] item = line[index:index+iLength]
...@@ -305,6 +305,10 @@ class PrmtopLoader(object): ...@@ -305,6 +305,10 @@ class PrmtopLoader(object):
return self._nonbondTerms return self._nonbondTerms
except AttributeError: except AttributeError:
pass pass
# Check if there are any non-zero HBOND terms
for x, y in zip(self._raw_data['HBOND_ACOEF'], self._raw_data['HBOND_BCOEF']):
if float(x) or float(y):
raise Exception('10-12 interactions are not supported')
self._nonbondTerms=[] self._nonbondTerms=[]
lengthConversionFactor = units.angstrom.conversion_factor_to(units.nanometer) lengthConversionFactor = units.angstrom.conversion_factor_to(units.nanometer)
energyConversionFactor = units.kilocalorie_per_mole.conversion_factor_to(units.kilojoule_per_mole) energyConversionFactor = units.kilocalorie_per_mole.conversion_factor_to(units.kilojoule_per_mole)
...@@ -333,6 +337,7 @@ class PrmtopLoader(object): ...@@ -333,6 +337,7 @@ class PrmtopLoader(object):
for i in range(numTypes): for i in range(numTypes):
for j in range(numTypes): for j in range(numTypes):
index = int(self._raw_data['NONBONDED_PARM_INDEX'][numTypes*i+j]) - 1 index = int(self._raw_data['NONBONDED_PARM_INDEX'][numTypes*i+j]) - 1
if index < 0: continue
rij = type_parameters[i][0] + type_parameters[j][0] rij = type_parameters[i][0] + type_parameters[j][0]
wdij = sqrt(type_parameters[i][1] * type_parameters[j][1]) wdij = sqrt(type_parameters[i][1] * type_parameters[j][1])
a = float(self._raw_data['LENNARD_JONES_ACOEF'][index]) a = float(self._raw_data['LENNARD_JONES_ACOEF'][index])
...@@ -477,6 +482,7 @@ class PrmtopLoader(object): ...@@ -477,6 +482,7 @@ class PrmtopLoader(object):
typ1 = atomTypeIndexes[iAtom] - 1 typ1 = atomTypeIndexes[iAtom] - 1
typ2 = atomTypeIndexes[lAtom] - 1 typ2 = atomTypeIndexes[lAtom] - 1
idx = nbidx[numTypes*typ1+typ2] - 1 idx = nbidx[numTypes*typ1+typ2] - 1
if idx < 0: continue
a = parm_acoef[idx] a = parm_acoef[idx]
b = parm_bcoef[idx] b = parm_bcoef[idx]
try: try:
...@@ -645,7 +651,7 @@ class PrmtopLoader(object): ...@@ -645,7 +651,7 @@ class PrmtopLoader(object):
# AMBER System builder (based on, but not identical to, systemManager from 'zander') # AMBER System builder (based on, but not identical to, systemManager from 'zander')
#============================================================================================= #=============================================================================================
def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmodel=None, def readAmberSystem(topology, prmtop_filename=None, prmtop_loader=None, shake=None, gbmodel=None,
soluteDielectric=1.0, solventDielectric=78.5, soluteDielectric=1.0, solventDielectric=78.5,
implicitSolventKappa=0.0*(1/units.nanometer), nonbondedCutoff=None, implicitSolventKappa=0.0*(1/units.nanometer), nonbondedCutoff=None,
nonbondedMethod='NoCutoff', scee=None, scnb=None, mm=None, verbose=False, nonbondedMethod='NoCutoff', scee=None, scnb=None, mm=None, verbose=False,
...@@ -653,6 +659,9 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode ...@@ -653,6 +659,9 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
""" """
Create an OpenMM System from an Amber prmtop file. Create an OpenMM System from an Amber prmtop file.
REQUIRED ARGUMENT
topology (forcefield.Topology) The topology for the system that is about
to be created
ARGUMENTS (specify one or the other, but not both) ARGUMENTS (specify one or the other, but not both)
prmtop_filename (String) - name of Amber prmtop file (new-style only) prmtop_filename (String) - name of Amber prmtop file (new-style only)
prmtop_loader (PrmtopLoader) - the loaded prmtop file prmtop_loader (PrmtopLoader) - the loaded prmtop file
...@@ -733,7 +742,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode ...@@ -733,7 +742,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
system.addParticle(mass) system.addParticle(mass)
# Add constraints. # Add constraints.
isWater = [prmtop.getResidueLabel(i) in ('WAT', 'TP4', 'TP5', 'T4E') for i in range(prmtop.getNumAtoms())] isWater = [prmtop.getResidueLabel(i) in ('WAT', 'HOH', 'TP4', 'TP5', 'T4E') for i in range(prmtop.getNumAtoms())]
if shake in ('h-bonds', 'all-bonds', 'h-angles'): if shake in ('h-bonds', 'all-bonds', 'h-angles'):
for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH(): for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH():
system.addConstraint(iAtom, jAtom, rMin) system.addConstraint(iAtom, jAtom, rMin)
...@@ -768,13 +777,14 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode ...@@ -768,13 +777,14 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
distance = c[2].value_in_unit(units.nanometer) distance = c[2].value_in_unit(units.nanometer)
atomConstraints[c[0]].append((c[1], distance)) atomConstraints[c[0]].append((c[1], distance))
atomConstraints[c[1]].append((c[0], distance)) atomConstraints[c[1]].append((c[0], distance))
topatoms = list(topology.atoms())
for (iAtom, jAtom, kAtom, k, aMin) in prmtop.getAngles(): for (iAtom, jAtom, kAtom, k, aMin) in prmtop.getAngles():
if shake == 'h-angles': if shake == 'h-angles':
type1 = prmtop.getAtomType(iAtom) atomI = topatoms[iAtom]
type2 = prmtop.getAtomType(jAtom) atomJ = topatoms[jAtom]
type3 = prmtop.getAtomType(kAtom) atomK = topatoms[kAtom]
numH = len([type for type in (type1, type3) if type.startswith('H')]) numH = ((atomI.element.atomic_number == 1) + (atomK.element.atomic_number == 1))
constrained = (numH == 2 or (numH == 1 and type2.startswith('O'))) constrained = (numH == 2 or (numH == 1 and atomJ.element is elem.oxygen))
else: else:
constrained = False constrained = False
if constrained: if constrained:
...@@ -870,6 +880,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode ...@@ -870,6 +880,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
for i in range(numTypes): for i in range(numTypes):
for j in range(numTypes): for j in range(numTypes):
idx = nbidx[numTypes*i+j] - 1 idx = nbidx[numTypes*i+j] - 1
if idx < 0: continue
acoef[i+numTypes*j] = sqrt(parm_acoef[idx]) * afac acoef[i+numTypes*j] = sqrt(parm_acoef[idx]) * afac
bcoef[i+numTypes*j] = parm_bcoef[idx] * bfac bcoef[i+numTypes*j] = parm_bcoef[idx] * bfac
if has_1264: if has_1264:
...@@ -878,6 +889,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode ...@@ -878,6 +889,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
for i in range(numTypes): for i in range(numTypes):
for j in range(numTypes): for j in range(numTypes):
idx = nbidx[numTypes*i+j] - 1 idx = nbidx[numTypes*i+j] - 1
if idx < 0: continue
ccoef[i+numTypes*j] = parm_ccoef[idx] * cfac ccoef[i+numTypes*j] = parm_ccoef[idx] * cfac
cforce = mm.CustomNonbondedForce('(a/r6)^2-b/r6-c/r^4; r6=r^6;' cforce = mm.CustomNonbondedForce('(a/r6)^2-b/r6-c/r^4; r6=r^6;'
'a=acoef(type1, type2);' 'a=acoef(type1, type2);'
...@@ -911,6 +923,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode ...@@ -911,6 +923,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
for i in range(numTypes): for i in range(numTypes):
for j in range(numTypes): for j in range(numTypes):
idx = nbidx[numTypes*i+j] - 1 idx = nbidx[numTypes*i+j] - 1
if idx < 0: continue
ccoef[i+numTypes*j] = parm_ccoef[idx] * cfac ccoef[i+numTypes*j] = parm_ccoef[idx] * cfac
cforce = mm.CustomNonbondedForce('-c/r^4; c=ccoef(type1, type2)') cforce = mm.CustomNonbondedForce('-c/r^4; c=ccoef(type1, type2)')
cforce.addTabulatedFunction('ccoef', cforce.addTabulatedFunction('ccoef',
......
...@@ -375,12 +375,13 @@ class AtomList(TrackedList): ...@@ -375,12 +375,13 @@ class AtomList(TrackedList):
class Residue(object): class Residue(object):
""" Residue class """ """ Residue class """
def __init__(self, resname, idx): def __init__(self, resname, idx, inscode=''):
self.resname = resname self.resname = resname
self.idx = idx self.idx = idx
self.atoms = [] self.atoms = []
self.resnum = None # Numbered based on SYSTEM name self.resnum = None # Numbered based on SYSTEM name
self.system = None self.system = None
self.inscode = inscode
def add_atom(self, atom): def add_atom(self, atom):
if self.system is None: if self.system is None:
...@@ -416,7 +417,7 @@ class Residue(object): ...@@ -416,7 +417,7 @@ class Residue(object):
return self.resname == thing.resname and self.idx == thing.idx return self.resname == thing.resname and self.idx == thing.idx
if isinstance(thing, tuple) or isinstance(thing, list): if isinstance(thing, tuple) or isinstance(thing, list):
# Must be resnum, resname # Must be resnum, resname
return thing == (self.resname, self.idx) return thing == (self.resname, self.idx, self.inscode)
return False # No other type can be equal. return False # No other type can be equal.
def __ne__(self, thing): def __ne__(self, thing):
...@@ -447,7 +448,7 @@ class ResidueList(list): ...@@ -447,7 +448,7 @@ class ResidueList(list):
resnum += 1 resnum += 1
def add_atom(self, system, resnum, resname, name, def add_atom(self, system, resnum, resname, name,
attype, charge, mass, props=None): attype, charge, mass, inscode, props=None):
""" """
Adds an atom to the list of residues. If the residue is not the same as Adds an atom to the list of residues. If the residue is not the same as
the last residue that was created, a new Residue is created and added the last residue that was created, a new Residue is created and added
...@@ -461,25 +462,22 @@ class ResidueList(list): ...@@ -461,25 +462,22 @@ class ResidueList(list):
- attype (int or str) : Type of the atom - attype (int or str) : Type of the atom
- charge (float) : Partial atomic charge of the atom - charge (float) : Partial atomic charge of the atom
- mass (float) : Mass (amu) of the atom - mass (float) : Mass (amu) of the atom
- inscode (str) : Insertion code, if it is specified
Returns: Returns:
The Atom instance created and added to the list of residues The Atom instance created and added to the list of residues
""" """
if self._last_residue is None: lr = self._last_residue
res = self._last_residue = Residue(resname, resnum) if lr is None:
res = self._last_residue = Residue(resname, resnum, inscode)
list.append(self, res) list.append(self, res)
elif (self._last_residue != (resname, resnum) or elif (lr.resname != resname or lr.idx != resname or
system != self._last_residue.system): lr.inscode != inscode or system != lr.system):
if (self._last_residue.idx == resnum and res = self._last_residue = Residue(resname, resnum, inscode)
self._last_residue.system == system): res.system = system
lresname = self._last_residue.resname
warnings.warn('Residue %d split into separate residues %s '
'and %s' % (resnum, lresname, resname),
SplitResidueWarning)
res = self._last_residue = Residue(resname, resnum)
list.append(self, res) list.append(self, res)
else: else:
res = self._last_residue res = lr
atom = Atom(system, name, attype, float(charge), float(mass), props) atom = Atom(system, name, attype, float(charge), float(mass), props)
res.add_atom(atom) res.add_atom(atom)
return atom return atom
......
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