Commit 4941cc7d authored by Justin MacCallum's avatar Justin MacCallum
Browse files

Removed trailing whitespace

parent 3862202e
......@@ -35,4 +35,4 @@ else:
from simtk.openmm.openmm import *
from simtk.openmm.vec3 import Vec3
pluginLoadedLibNames = Platform.loadPluginsFromDirectory(Platform.getDefaultPluginsDirectory())
\ No newline at end of file
pluginLoadedLibNames = Platform.loadPluginsFromDirectory(Platform.getDefaultPluginsDirectory())
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012 Stanford University and the Authors.
Authors: Peter Eastman
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -40,15 +40,15 @@ except:
class AmberInpcrdFile(object):
"""AmberInpcrdFile parses an AMBER inpcrd file and loads the data stored in it."""
def __init__(self, file, loadVelocities=False, loadBoxVectors=False):
"""Load an inpcrd file.
An inpcrd file contains atom positions and, optionally, velocities and periodic box dimensions.
Unfortunately, it is sometimes impossible to determine from the file itself exactly what data
it contains. You therefore must specify in advance what data to load. It is stored into this
object's "positions", "velocities", and "boxVectors" fields.
Parameters:
- file (string) the name of the file to load
- loadVelocities (boolean=False) whether to load velocities from the file
......@@ -78,7 +78,7 @@ class AmberInpcrdFile(object):
def getPositions(self, asNumpy=False):
"""Get the atomic positions.
Parameters:
- asNumpy (boolean=False) if true, the values are returned as a numpy array instead of a list of Vec3s
"""
......@@ -87,10 +87,10 @@ class AmberInpcrdFile(object):
self._numpyPositions = Quantity(numpy.array(self.positions.value_in_unit(nanometers)), nanometers)
return self._numpyPositions
return self.positions
def getVelocities(self, asNumpy=False):
"""Get the atomic velocities.
Parameters:
- asNumpy (boolean=False) if true, the vectors are returned as numpy arrays instead of Vec3s
"""
......@@ -99,10 +99,10 @@ class AmberInpcrdFile(object):
self._numpyVelocities = Quantity(numpy.array(self.velocities.value_in_unit(nanometers/picoseconds)), nanometers/picoseconds)
return self._numpyVelocities
return self.velocities
def getBoxVectors(self, asNumpy=False):
"""Get the periodic box vectors.
Parameters:
- asNumpy (boolean=False) if true, the values are returned as a numpy array instead of a list of Vec3s
"""
......@@ -114,4 +114,4 @@ class AmberInpcrdFile(object):
self._numpyBoxVectors.append(Quantity(numpy.array(self.boxVectors[2].value_in_unit(nanometers)), nanometers))
return self._numpyBoxVectors
return self.boxVectors
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012 Stanford University and the Authors.
Authors: Peter Eastman
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -48,15 +48,15 @@ GBn = object()
class AmberPrmtopFile(object):
"""AmberPrmtopFile parses an AMBER prmtop file and constructs a Topology and (optionally) an OpenMM System from it."""
def __init__(self, file):
"""Load a prmtop file."""
top = Topology()
## The Topology read from the prmtop file
self.topology = top
# Load the prmtop file
prmtop = amber_file_parser.PrmtopLoader(file)
self._prmtop = prmtop
......@@ -82,7 +82,7 @@ class AmberPrmtopFile(object):
atomName = atomReplacements[atomName]
# Try to guess the element.
upper = atomName.upper()
if upper.startswith('CL'):
element = elem.chlorine
......@@ -96,17 +96,17 @@ class AmberPrmtopFile(object):
except KeyError:
element = None
top.addAtom(atomName, element, r)
# Add bonds to the topology
atoms = list(top.atoms())
for bond in prmtop.getBondsWithH():
top.addBond(atoms[bond[0]], atoms[bond[1]])
for bond in prmtop.getBondsNoH():
top.addBond(atoms[bond[0]], atoms[bond[1]])
# Set the periodic box size.
if prmtop.getIfBox():
top.setUnitCellDimensions(tuple(x.value_in_unit(unit.nanometer) for x in prmtop.getBoxBetaAndDimensions()[1:4])*unit.nanometer)
......@@ -114,7 +114,7 @@ class AmberPrmtopFile(object):
constraints=None, rigidWater=True, implicitSolvent=None, soluteDielectric=1.0, solventDielectric=78.5, removeCMMotion=True,
ewaldErrorTolerance=0.0005):
"""Construct an OpenMM System representing the topology described by this prmtop file.
Parameters:
- nonbondedMethod (object=NoCutoff) The method to use for nonbonded interactions. Allowed values are
NoCutoff, CutoffNonPeriodic, CutoffPeriodic, Ewald, or PME.
......@@ -168,4 +168,4 @@ class AmberPrmtopFile(object):
force.setEwaldErrorTolerance(ewaldErrorTolerance)
if removeCMMotion:
sys.addForce(mm.CMMotionRemover())
return sys
\ No newline at end of file
return sys
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012 Stanford University and the Authors.
Authors: Peter Eastman
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -40,18 +40,18 @@ from simtk.unit import picoseconds, nanometers, angstroms, is_quantity, norm
class DCDFile(object):
"""DCDFile provides methods for creating DCD files.
DCD is a file format for storing simulation trajectories. It is supported by many programs, such
as CHARMM, NAMD, and X-PLOR. Note, however, that different programs produce subtly different
versions of the format. This class generates the CHARMM version. Also note that there is no
standard byte ordering (big-endian or little-endian) for this format. This class always generates
files with little-endian ordering.
To use this class, create a DCDFile object, then call writeModel() once for each model in the file."""
def __init__(self, file, topology, dt, firstStep=0, interval=1):
"""Create a DCD file and write out the header.
Parameters:
- file (file) A file to write to
- topology (Topology) The Topology defining the molecular system being written
......@@ -76,10 +76,10 @@ class DCDFile(object):
header += struct.pack('<80s', 'Created '+time.asctime(time.localtime(time.time())))
header += struct.pack('<4i', 164, 4, len(list(topology.atoms())), 4)
file.write(header)
def writeModel(self, positions, unitCellDimensions=None):
"""Write out a model to the DCD file.
Parameters:
- positions (list) The list of atomic positions to write
- unitCellDimensions (Vec3=None) The dimensions of the crystallographic unit cell. If None, the dimensions specified in
......@@ -87,7 +87,7 @@ class DCDFile(object):
represent a periodic system.
"""
if len(list(self._topology.atoms())) != len(positions):
raise ValueError('The number of positions must match the number of atoms')
raise ValueError('The number of positions must match the number of atoms')
if is_quantity(positions):
positions = positions.value_in_unit(nanometers)
if any(math.isnan(norm(pos)) for pos in positions):
......@@ -95,17 +95,17 @@ class DCDFile(object):
if any(math.isinf(norm(pos)) for pos in positions):
raise ValueError('Particle position is infinite')
file = self._file
# Update the header.
self._modelCount += 1
file.seek(8, os.SEEK_SET)
file.write(struct.pack('<i', self._modelCount))
file.seek(20, os.SEEK_SET)
file.write(struct.pack('<i', self._firstStep+self._modelCount*self._interval))
# Write the data.
file.seek(0, os.SEEK_END)
boxSize = self._topology.getUnitCellDimensions()
if boxSize is not None:
......
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012 Stanford University and the Authors.
Authors: Peter Eastman
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -34,16 +34,16 @@ __version__ = "1.0"
import simtk.openmm as mm
from simtk.openmm.app import DCDFile
from simtk.unit import nanometer
class DCDReporter(object):
"""DCDReporter outputs a series of frames from a Simulation to a DCD file.
To use it, create a DCDReporter, then add it to the Simulation's list of reporters.
"""
def __init__(self, file, reportInterval):
"""Create a DCDReporter.
Parameters:
- file (string) The file to write to
- reportInterval (int) The interval (in time steps) at which to write frames
......@@ -51,10 +51,10 @@ class DCDReporter(object):
self._reportInterval = reportInterval
self._out = open(file, 'wb')
self._dcd = None
def describeNextReport(self, simulation):
"""Get information about the next report this object will generate.
Parameters:
- simulation (Simulation) The Simulation to generate a report for
Returns: A five element tuple. The first element is the number of steps until the
......@@ -63,10 +63,10 @@ class DCDReporter(object):
"""
steps = self._reportInterval - simulation.currentStep%self._reportInterval
return (steps, True, False, False, False)
def report(self, simulation, state):
"""Generate a report.
Parameters:
- simulation (Simulation) The Simulation to generate a report for
- state (State) The current state of the simulation
......@@ -75,6 +75,6 @@ class DCDReporter(object):
self._dcd = DCDFile(self._out, simulation.topology, simulation.integrator.getStepSize(), 0, self._reportInterval)
a,b,c = state.getPeriodicBoxVectors()
self._dcd.writeModel(state.getPositions(), mm.Vec3(a[0].value_in_unit(nanometer), b[1].value_in_unit(nanometer), c[2].value_in_unit(nanometer))*nanometer)
def __del__(self):
self._out.close()
......@@ -12,7 +12,7 @@ Portions copyright (c) 2012 Stanford University and the Authors.
Authors: Christopher M. Bruns
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -38,13 +38,13 @@ from simtk.unit import daltons
class Element:
"""An Element represents a chemical element.
The simtk.openmm.app.element module contains objects for all the standard chemical elements,
such as element.hydrogen or element.carbon. You can also call the static method Element.getBySymbol() to
look up the Element with a particular chemical symbol."""
_elements_by_symbol = {}
def __init__(self, number, name, symbol, mass):
## The atomic number of the element
self.atomic_number = number
......
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012-2013 Stanford University and the Authors.
Authors: Peter Eastman, Mark Friedrichs
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -63,7 +63,7 @@ class ForceField(object):
def __init__(self, *files):
"""Load one or more XML files and create a ForceField object based on them.
Parameters:
- files A list of XML files defining the force field. Each entry may be an absolute file path, a path relative to the
current working directory, or a path relative to this module's data subdirectory (for built in force fields).
......@@ -80,9 +80,9 @@ class ForceField(object):
except IOError:
tree = etree.parse(os.path.join(os.path.dirname(__file__), 'data', file))
root = tree.getroot()
# Load the atom types.
if tree.getroot().find('AtomTypes') is not None:
for type in tree.getroot().find('AtomTypes').findall('Type'):
element = None
......@@ -92,9 +92,9 @@ class ForceField(object):
if name in self._atomTypes:
raise ValueError('Found multiple definitions for atom type: '+name)
self._atomTypes[name] = (type.attrib['class'], float(type.attrib['mass']), element)
# Load the residue templates.
if tree.getroot().find('Residues') is not None:
for residue in root.find('Residues').findall('Residue'):
resName = residue.attrib['name']
......@@ -119,9 +119,9 @@ class ForceField(object):
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:
......@@ -131,15 +131,15 @@ class ForceField(object):
self._atomClasses[atomClass] = typeSet
typeSet.add(type)
self._atomClasses[''].add(type)
# Load force definitions
for child in root:
if child.tag in parsers:
parsers[child.tag](child, self)
# Load scripts
for node in tree.getroot().findall('Script'):
self._scripts.append(node.text)
......@@ -181,8 +181,8 @@ class ForceField(object):
torsion.phase.append(float(attrib['phase%d'%index]))
torsion.k.append(float(attrib['k%d'%index]))
index += 1
return torsion
return torsion
class _SystemData:
"""Inner class used to encapsulate data about the system being created."""
def __init__(self):
......@@ -221,7 +221,7 @@ class ForceField(object):
self.atom2 = atom2
self.isConstrained = False
self.length = 0.0
class _VirtualSiteData:
"""Inner class used to encapsulate data about a virtual site."""
def __init__(self, node):
......@@ -244,7 +244,7 @@ class ForceField(object):
def createSystem(self, topology, nonbondedMethod=NoCutoff, nonbondedCutoff=1.0*unit.nanometer,
constraints=None, rigidWater=True, removeCMMotion=True, **args):
"""Construct an OpenMM System representing a Topology with this force field.
Parameters:
- topology (Topology) The Topology for which to create a System
- nonbondedMethod (object=NoCutoff) The method to use for nonbonded interactions. Allowed values are
......@@ -262,12 +262,12 @@ class ForceField(object):
data.atoms = list(topology.atoms())
# Make a list of all bonds
for bond in topology.bonds():
data.bonds.append(ForceField._BondData(bond[0].index, bond[1].index))
# Record which atoms are bonded to each other atom
bondedToAtom = []
for i in range(len(data.atoms)):
bondedToAtom.append(set())
......@@ -280,7 +280,7 @@ class ForceField(object):
data.atomBonds[bond.atom2].append(i)
# Find the template matching each residue and assign atom types.
for chain in topology.chains():
for res in chain.residues():
template = None
......@@ -301,13 +301,13 @@ class ForceField(object):
data.virtualSites[atom] = site
# Create the System and add atoms
sys = mm.System()
for atom in topology.atoms():
sys.addParticle(self._atomTypes[data.atomType[atom]][1])
# Set periodic boundary conditions.
boxSize = topology.getUnitCellDimensions()
if boxSize is not None:
sys.setDefaultPeriodicBoxVectors((boxSize[0], 0, 0), (0, boxSize[1], 0), (0, 0, boxSize[2]))
......@@ -315,7 +315,7 @@ class ForceField(object):
raise ValueError('Requested periodic boundary conditions for a Topology that does not specify periodic box dimensions')
# Make a list of all unique angles
uniqueAngles = set()
for bond in data.bonds:
for atom in bondedToAtom[bond.atom1]:
......@@ -331,9 +331,9 @@ class ForceField(object):
else:
uniqueAngles.add((atom, bond.atom2, bond.atom1))
data.angles = sorted(list(uniqueAngles))
# Make a list of all unique proper torsions
uniquePropers = set()
for angle in data.angles:
for atom in bondedToAtom[angle[0]]:
......@@ -349,17 +349,17 @@ class ForceField(object):
else:
uniquePropers.add((atom, angle[2], angle[1], angle[0]))
data.propers = sorted(list(uniquePropers))
# Make a list of all unique improper torsions
for atom in range(len(bondedToAtom)):
bondedTo = bondedToAtom[atom]
if len(bondedTo) > 2:
for subset in itertools.combinations(bondedTo, 3):
data.impropers.append((atom, subset[0], subset[1], subset[2]))
# Identify bonds that should be implemented with constraints
if constraints == AllBonds or constraints == HAngles:
for bond in data.bonds:
bond.isConstrained = True
......@@ -374,9 +374,9 @@ class ForceField(object):
atom2 = data.atoms[bond.atom2]
if atom1.residue.name == 'HOH' and atom2.residue.name == 'HOH':
bond.isConstrained = True
# Identify angles that should be implemented with constraints
if constraints == HAngles:
for angle in data.angles:
atom1 = data.atoms[angle[0]]
......@@ -398,9 +398,9 @@ class ForceField(object):
atom3 = data.atoms[angle[2]]
if atom1.residue.name == 'HOH' and atom2.residue.name == 'HOH' and atom3.residue.name == 'HOH':
data.isAngleConstrained[i] = True
# Add virtual sites
for atom in data.virtualSites:
site = data.virtualSites[atom]
index = atom.index
......@@ -410,22 +410,22 @@ class ForceField(object):
sys.setVirtualSite(index, mm.ThreeParticleAverageSite(index+site.atoms[0], index+site.atoms[1], index+site.atoms[2], site.weights[0], site.weights[1], site.weights[2]))
elif site.type == 'outOfPlane':
sys.setVirtualSite(index, mm.OutOfPlaneSite(index+site.atoms[0], index+site.atoms[1], index+site.atoms[2], site.weights[0], site.weights[1], site.weights[2]))
# Add forces to the System
for force in self._forces:
force.createForce(sys, data, nonbondedMethod, nonbondedCutoff, args)
if removeCMMotion:
sys.addForce(mm.CMMotionRemover())
# Let generators do postprocessing
for force in self._forces:
if 'postprocessSystem' in dir(force):
force.postprocessSystem(sys, data, args)
# Execute scripts found in the XML files.
for script in self._scripts:
exec script
return sys
......@@ -445,7 +445,7 @@ def _createResidueSignature(elements):
for c in counts:
sig.append((c, counts[c]))
sig.sort(key=lambda x: -x[0].mass)
# Convert it to a string.
s = ''
......@@ -456,7 +456,7 @@ def _createResidueSignature(elements):
def _matchResidue(res, template, bondedToAtom):
"""Determine whether a residue matches a template and return a list of corresponding atoms.
Parameters:
- res (Residue) The residue to check
- template (_TemplateData) The template to compare it to
......@@ -469,9 +469,9 @@ def _matchResidue(res, template, bondedToAtom):
return None
matches = len(atoms)*[0]
hasMatch = len(atoms)*[False]
# Translate from global to local atom indices, and record the bonds for each atom.
renumberAtoms = {}
for i in range(len(atoms)):
renumberAtoms[atoms[i].index] = i
......@@ -496,11 +496,11 @@ def _findAtomMatches(atoms, template, bondedTo, externalBonds, matches, hasMatch
atom = template.atoms[i]
if (atom.element == elem or (atom.element is None and atom.name == name)) and not hasMatch[i] and len(atom.bondedTo) == len(bondedTo[position]) and atom.externalBonds == externalBonds[position]:
# See if the bonds for this identification are consistent
allBondsMatch = all((bonded > position or matches[bonded] in atom.bondedTo for bonded in bondedTo[position]))
if allBondsMatch:
# This is a possible match, so trying matching the rest of the residue.
matches[position] = i
hasMatch[i] = True
if _findAtomMatches(atoms, template, bondedTo, externalBonds, matches, hasMatch, position+1):
......@@ -517,13 +517,13 @@ def _findAtomMatches(atoms, template, bondedTo, externalBonds, matches, hasMatch
## @private
class HarmonicBondGenerator:
"""A HarmonicBondGenerator constructs a HarmonicBondForce."""
def __init__(self):
self.types1 = []
self.types2 = []
self.length = []
self.k = []
@staticmethod
def parseElement(element, ff):
generator = HarmonicBondGenerator()
......@@ -535,7 +535,7 @@ class HarmonicBondGenerator:
generator.types2.append(types[1])
generator.length.append(float(bond.attrib['length']))
generator.k.append(float(bond.attrib['k']))
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
existing = [sys.getForce(i) for i in range(sys.getNumForces())]
existing = [f for f in existing if type(f) == mm.HarmonicBondForce]
......@@ -564,14 +564,14 @@ parsers["HarmonicBondForce"] = HarmonicBondGenerator.parseElement
## @private
class HarmonicAngleGenerator:
"""A HarmonicAngleGenerator constructs a HarmonicAngleForce."""
def __init__(self):
self.types1 = []
self.types2 = []
self.types3 = []
self.angle = []
self.k = []
@staticmethod
def parseElement(element, ff):
generator = HarmonicAngleGenerator()
......@@ -584,7 +584,7 @@ class HarmonicAngleGenerator:
generator.types3.append(types[2])
generator.angle.append(float(angle.attrib['angle']))
generator.k.append(float(angle.attrib['k']))
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
existing = [sys.getForce(i) for i in range(sys.getNumForces())]
existing = [f for f in existing if type(f) == mm.HarmonicAngleForce]
......@@ -604,7 +604,7 @@ class HarmonicAngleGenerator:
if (type1 in types1 and type2 in types2 and type3 in types3) or (type1 in types3 and type2 in types2 and type3 in types1):
if isConstrained:
# Find the two bonds that make this angle.
bond1 = None
bond2 = None
for bond in data.atomBonds[angle[1]]:
......@@ -614,9 +614,9 @@ class HarmonicAngleGenerator:
bond1 = bond
elif atom1 == angle[2] or atom2 == angle[2]:
bond2 = bond
# Compute the distance between atoms and add a constraint
if bond1 is not None and bond2 is not None:
l1 = data.bonds[bond1].length
l2 = data.bonds[bond2].length
......@@ -646,11 +646,11 @@ class PeriodicTorsion:
## @private
class PeriodicTorsionGenerator:
"""A PeriodicTorsionGenerator constructs a PeriodicTorsionForce."""
def __init__(self):
self.proper = []
self.improper = []
@staticmethod
def parseElement(element, ff):
generator = PeriodicTorsionGenerator()
......@@ -664,7 +664,7 @@ class PeriodicTorsionGenerator:
torsion = ff._parseTorsion(torsion)
if torsion is not None:
generator.improper.append(torsion)
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
existing = [sys.getForce(i) for i in range(sys.getNumForces())]
existing = [f for f in existing if type(f) == mm.PeriodicTorsionForce]
......@@ -745,11 +745,11 @@ class RBTorsion:
## @private
class RBTorsionGenerator:
"""An RBTorsionGenerator constructs an RBTorsionForce."""
def __init__(self):
self.proper = []
self.improper = []
@staticmethod
def parseElement(element, ff):
generator = RBTorsionGenerator()
......@@ -763,7 +763,7 @@ class RBTorsionGenerator:
types = ff._findAtomTypes(torsion, 4)
if None not in types:
generator.improper.append(RBTorsion(types, [float(torsion.attrib['c'+str(i)]) for i in range(6)]))
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
existing = [sys.getForce(i) for i in range(sys.getNumForces())]
existing = [f for f in existing if type(f) == mm.RBTorsionForce]
......@@ -841,11 +841,11 @@ class CMAPTorsion:
## @private
class CMAPTorsionGenerator:
"""A CMAPTorsionGenerator constructs a CMAPTorsionForce."""
def __init__(self):
self.torsions = []
self.maps = []
@staticmethod
def parseElement(element, ff):
generator = CMAPTorsionGenerator()
......@@ -861,7 +861,7 @@ class CMAPTorsionGenerator:
types = ff._findAtomTypes(torsion, 5)
if None not in types:
generator.torsions.append(CMAPTorsion(types, int(torsion.attrib['map'])))
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
existing = [sys.getForce(i) for i in range(sys.getNumForces())]
existing = [f for f in existing if type(f) == mm.CMAPTorsionForce]
......@@ -872,9 +872,9 @@ class CMAPTorsionGenerator:
force = existing[0]
for map in self.maps:
force.addMap(int(sqrt(len(map))), map)
# Find all chains of length 5
uniqueTorsions = set()
for torsion in data.propers:
for bond in (data.bonds[x] for x in data.atomBonds[torsion[0]]):
......@@ -921,7 +921,7 @@ parsers["CMAPTorsionForce"] = CMAPTorsionGenerator.parseElement
## @private
class NonbondedGenerator:
"""A NonbondedGenerator constructs a NonbondedForce."""
def __init__(self, coulomb14scale, lj14scale):
self.coulomb14scale = coulomb14scale
self.lj14scale = lj14scale
......@@ -937,14 +937,14 @@ class NonbondedGenerator:
# 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')
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
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
methodMap = {NoCutoff:mm.NonbondedForce.NoCutoff,
CutoffNonPeriodic:mm.NonbondedForce.CutoffNonPeriodic,
......@@ -952,7 +952,7 @@ class NonbondedGenerator:
Ewald:mm.NonbondedForce.Ewald,
PME:mm.NonbondedForce.PME}
if nonbondedMethod not in methodMap:
raise ValueError('Illegal nonbonded method for NonbondedForce')
raise ValueError('Illegal nonbonded method for NonbondedForce')
force = mm.NonbondedForce()
for atom in data.atoms:
t = data.atomType[atom]
......@@ -966,7 +966,7 @@ class NonbondedGenerator:
if 'ewaldErrorTolerance' in args:
force.setEwaldErrorTolerance(args['ewaldErrorTolerance'])
sys.addForce(force)
def postprocessSystem(self, sys, data, args):
# Create exceptions based on bonds, virtual sites, and Drude particles.
bondIndices = []
......@@ -996,7 +996,7 @@ class NonbondedGenerator:
if drude2 is not None:
bondIndices.append((atom1, drude2))
nonbonded = [f for f in sys.getForces() if isinstance(f, mm.NonbondedForce)][0]
nonbonded.createExceptionsFromBonds(bondIndices, self.coulomb14scale, self.lj14scale)
nonbonded.createExceptionsFromBonds(bondIndices, self.coulomb14scale, self.lj14scale)
parsers["NonbondedForce"] = NonbondedGenerator.parseElement
......@@ -1004,7 +1004,7 @@ parsers["NonbondedForce"] = NonbondedGenerator.parseElement
## @private
class GBSAOBCGenerator:
"""A GBSAOBCGenerator constructs a GBSAOBCForce."""
def __init__(self):
self.typeMap = {}
......@@ -1023,13 +1023,13 @@ class GBSAOBCGenerator:
values = (float(atom.attrib['charge']), float(atom.attrib['radius']), float(atom.attrib['scale']))
for t in types[0]:
generator.typeMap[t] = values
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
methodMap = {NoCutoff:mm.NonbondedForce.NoCutoff,
CutoffNonPeriodic:mm.NonbondedForce.CutoffNonPeriodic,
CutoffPeriodic:mm.NonbondedForce.CutoffPeriodic}
if nonbondedMethod not in methodMap:
raise ValueError('Illegal nonbonded method for GBSAOBCForce')
raise ValueError('Illegal nonbonded method for GBSAOBCForce')
force = mm.GBSAOBCForce()
for atom in data.atoms:
t = data.atomType[atom]
......@@ -1037,7 +1037,7 @@ class GBSAOBCGenerator:
values = self.typeMap[t]
force.addParticle(values[0], values[1], values[2])
else:
raise ValueError('No GBSAOBC parameters defined for atom type '+t)
raise ValueError('No GBSAOBC parameters defined for atom type '+t)
force.setNonbondedMethod(methodMap[nonbondedMethod])
force.setCutoffDistance(nonbondedCutoff)
if 'soluteDielectric' in args:
......@@ -1048,7 +1048,7 @@ class GBSAOBCGenerator:
def postprocessSystem(self, sys, data, args):
# Disable the reaction field approximation, since it produces bad results when combined with GB.
for force in sys.getForces():
if isinstance(force, mm.NonbondedForce):
force.setReactionFieldDielectric(1.0)
......@@ -1060,7 +1060,7 @@ parsers["GBSAOBCForce"] = GBSAOBCGenerator.parseElement
class GBVIGenerator:
"""A GBVIGenerator constructs a GBVIForce."""
def __init__(self,ff):
self.ff = ff
......@@ -1087,7 +1087,7 @@ class GBVIGenerator:
values = (float(atom.attrib['charge']), float(atom.attrib['radius']), float(atom.attrib['gamma']))
for t in types[0]:
generator.typeMap[t] = values
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
methodMap = {NoCutoff:mm.NonbondedForce.NoCutoff,
......@@ -1095,7 +1095,7 @@ class GBVIGenerator:
CutoffPeriodic:mm.NonbondedForce.CutoffPeriodic}
if nonbondedMethod not in methodMap:
raise ValueError('Illegal nonbonded method for GB/VI Force')
raise ValueError('Illegal nonbonded method for GB/VI Force')
# add particles
......@@ -1106,18 +1106,18 @@ class GBVIGenerator:
values = self.typeMap[t]
force.addParticle(values[0], values[1], values[2])
else:
raise ValueError('No GB/VI parameters defined for atom type '+t)
raise ValueError('No GB/VI parameters defined for atom type '+t)
# get HarmonicBond generator -- exit if not found
hbGenerator = 0
for generator in self.ff._forces:
if (generator.__class__.__name__ == 'HarmonicBondGenerator'):
if (generator.__class__.__name__ == 'HarmonicBondGenerator'):
hbGenerator = generator
break
if (hbGenerator == 0):
raise ValueError('HarmonicBondGenerator not found.')
raise ValueError('HarmonicBondGenerator not found.')
# add bonds
......@@ -1139,7 +1139,7 @@ class GBVIGenerator:
force.setBornRadiusScalingMethod(self.fixedParameters['scalingMethod'])
force.setQuinticLowerLimitFactor(self.fixedParameters['quinticLowerLimitFactor'])
force.setQuinticUpperBornRadiusLimit(self.fixedParameters['quinticUpperBornRadiusLimit'])
sys.addForce(force)
parsers["GBVIForce"] = GBVIGenerator.parseElement
......@@ -1147,14 +1147,14 @@ parsers["GBVIForce"] = GBVIGenerator.parseElement
## @private
class CustomBondGenerator:
"""A CustomBondGenerator constructs a CustomBondForce."""
def __init__(self):
self.types1 = []
self.types2 = []
self.globalParams = {}
self.perBondParams = []
self.paramValues = []
@staticmethod
def parseElement(element, ff):
generator = CustomBondGenerator()
......@@ -1170,7 +1170,7 @@ class CustomBondGenerator:
generator.types1.append(types[0])
generator.types2.append(types[1])
generator.paramValues.append([float(bond.attrib[param]) for param in generator.perBondParams])
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
force = mm.CustomBondForce(self.energy)
sys.addForce(force)
......@@ -1194,7 +1194,7 @@ parsers["CustomBondForce"] = CustomBondGenerator.parseElement
## @private
class CustomAngleGenerator:
"""A CustomAngleGenerator constructs a CustomAngleForce."""
def __init__(self):
self.types1 = []
self.types2 = []
......@@ -1202,7 +1202,7 @@ class CustomAngleGenerator:
self.globalParams = {}
self.perAngleParams = []
self.paramValues = []
@staticmethod
def parseElement(element, ff):
generator = CustomAngleGenerator()
......@@ -1219,7 +1219,7 @@ class CustomAngleGenerator:
generator.types2.append(types[1])
generator.types3.append(types[2])
generator.paramValues.append([float(angle.attrib[param]) for param in generator.perAngleParams])
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
force = mm.CustomAngleForce(self.energy)
sys.addForce(force)
......@@ -1256,13 +1256,13 @@ class CustomTorsion:
## @private
class CustomTorsionGenerator:
"""A CustomTorsionGenerator constructs a CustomTorsionForce."""
def __init__(self):
self.proper = []
self.improper = []
self.globalParams = {}
self.perTorsionParams = []
@staticmethod
def parseElement(element, ff):
generator = CustomTorsionGenerator()
......@@ -1281,7 +1281,7 @@ class CustomTorsionGenerator:
types = ff._findAtomTypes(torsion, 4)
if None not in types:
generator.improper.append(CustomTorsion(types, [float(torsion.attrib[param]) for param in generator.perTorsionParams]))
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
force = mm.CustomTorsionForce(self.energy)
sys.addForce(force)
......@@ -1346,7 +1346,7 @@ parsers["CustomTorsionForce"] = CustomTorsionGenerator.parseElement
## @private
class CustomGBGenerator:
"""A CustomGBGenerator constructs a CustomGBForce."""
def __init__(self):
self.typeMap = {}
self.globalParams = {}
......@@ -1380,13 +1380,13 @@ class CustomGBGenerator:
for function in element.findall("Function"):
values = [float(x) for x in function.text.split()]
generator.functions.append((function.attrib['name'], values, float(function.attrib['min']), float(function.attrib['max'])))
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
methodMap = {NoCutoff:mm.CustomGBForce.NoCutoff,
CutoffNonPeriodic:mm.CustomGBForce.CutoffNonPeriodic,
CutoffPeriodic:mm.CustomGBForce.CutoffPeriodic}
if nonbondedMethod not in methodMap:
raise ValueError('Illegal nonbonded method for CustomGBForce')
raise ValueError('Illegal nonbonded method for CustomGBForce')
force = mm.CustomGBForce()
for param in self.globalParams:
force.addGlobalParameter(param, self.globalParams[param])
......@@ -1404,7 +1404,7 @@ class CustomGBGenerator:
values = self.typeMap[t]
force.addParticle(self.typeMap[t])
else:
raise ValueError('No CustomGB parameters defined for atom type '+t)
raise ValueError('No CustomGB parameters defined for atom type '+t)
force.setNonbondedMethod(methodMap[nonbondedMethod])
force.setCutoffDistance(nonbondedCutoff)
sys.addForce(force)
......@@ -1435,7 +1435,7 @@ def countConstraint(data):
for (angle, isConstrained) in zip(data.angles, data.isAngleConstrained):
if (isConstrained):
angleCount += 1
print "Constraints bond=%d angle=%d total=%d" % (bondCount, angleCount, (bondCount+angleCount))
## @private
......@@ -1446,7 +1446,7 @@ class AmoebaBondGenerator:
"""An AmoebaBondGenerator constructs a AmoebaBondForce."""
#=============================================================================================
def __init__(self, cubic, quartic):
self.cubic = cubic
......@@ -1455,7 +1455,7 @@ class AmoebaBondGenerator:
self.types2 = []
self.length = []
self.k = []
#=============================================================================================
@staticmethod
......@@ -1463,7 +1463,7 @@ class AmoebaBondGenerator:
# <AmoebaBondForce bond-cubic="-25.5" bond-quartic="379.3125">
# <Bond class1="1" class2="2" length="0.1437" k="156900.0"/>
generator = AmoebaBondGenerator(float(element.attrib['bond-cubic']), float(element.attrib['bond-quartic']))
forceField._forces.append(generator)
for bond in element.findall('Bond'):
......@@ -1477,8 +1477,8 @@ class AmoebaBondGenerator:
outputString = "AmoebaBondGenerator: error getting types: %s %s" % (
bond.attrib['class1'],
bond.attrib['class2'])
raise ValueError(outputString)
raise ValueError(outputString)
#=============================================================================================
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
......@@ -1512,20 +1512,20 @@ class AmoebaBondGenerator:
force.addBond(bond.atom1, bond.atom2, self.length[i], self.k[i])
break
if (hit == 0):
if (hit == 0):
outputString = "AmoebaBondGenerator missing: types=[%5s %5s] atoms=[%6d %6d] " % (type1, type2, bond.atom1, bond.atom2)
raise ValueError(outputString)
raise ValueError(outputString)
parsers["AmoebaBondForce"] = AmoebaBondGenerator.parseElement
#=============================================================================================
# Add angle constraint
#=============================================================================================
def addAngleConstraint(angle, idealAngle, data, sys):
# Find the two bonds that make this angle.
bond1 = None
bond2 = None
for bond in data.atomBonds[angle[1]]:
......@@ -1535,9 +1535,9 @@ def addAngleConstraint(angle, idealAngle, data, sys):
bond1 = bond
elif atom1 == angle[2] or atom2 == angle[2]:
bond2 = bond
# Compute the distance between atoms and add a constraint
if bond1 is not None and bond2 is not None:
l1 = data.bonds[bond1].length
l2 = data.bonds[bond2].length
......@@ -1553,7 +1553,7 @@ class AmoebaAngleGenerator:
#=============================================================================================
"""An AmoebaAngleGenerator constructs a AmoebaAngleForce."""
#=============================================================================================
def __init__(self, forceField, cubic, quartic, pentic, sextic):
self.forceField = forceField
......@@ -1568,7 +1568,7 @@ class AmoebaAngleGenerator:
self.angle = []
self.k = []
#=============================================================================================
@staticmethod
......@@ -1605,13 +1605,13 @@ class AmoebaAngleGenerator:
angle.attrib['class1'],
angle.attrib['class2'],
angle.attrib['class3'])
raise ValueError(outputString)
raise ValueError(outputString)
#=============================================================================================
# createForce is bypassed here since the AmoebaOutOfPlaneBendForce generator must first execute
# and partition angles into in-plane and non-in-plane angles
#=============================================================================================
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
pass
......@@ -1619,7 +1619,7 @@ class AmoebaAngleGenerator:
# createForcePostOpBendAngle is called by AmoebaOutOfPlaneBendForce with the list of
# non-in-plane angles
#=============================================================================================
def createForcePostOpBendAngle(self, sys, data, nonbondedMethod, nonbondedCutoff, angleList, args):
# get force
......@@ -1674,26 +1674,26 @@ class AmoebaAngleGenerator:
angleValue = self.angle[i][numberOfHydrogens]
else:
outputString = "AmoebaAngleGenerator angle index=%d is out of range: [0, %5d] " % (numberOfHydrogens, lenAngle)
raise ValueError(outputString)
raise ValueError(outputString)
else:
angleValue = self.angle[i][0]
angleDict['idealAngle'] = angleValue
force.addAngle(angle[0], angle[1], angle[2], angleValue, self.k[i])
break
if (hit == 0):
if (hit == 0):
outputString = "AmoebaAngleGenerator missing types: [%s %s %s] for atoms: " % (type1, type2, type3)
outputString += getAtomPrint( data, angle[0] ) + ' '
outputString += getAtomPrint( data, angle[1] ) + ' '
outputString += getAtomPrint( data, angle[2] )
outputString += " indices: [%6d %6d %6d]" % (angle[0], angle[1], angle[2])
raise ValueError(outputString)
raise ValueError(outputString)
#=============================================================================================
# createForcePostOpBendInPlaneAngle is called by AmoebaOutOfPlaneBendForce with the list of
# in-plane angles
#=============================================================================================
def createForcePostOpBendInPlaneAngle(self, sys, data, nonbondedMethod, nonbondedCutoff, angleList, args):
# get force
......@@ -1715,7 +1715,7 @@ class AmoebaAngleGenerator:
force.setAmoebaGlobalInPlaneAngleSextic(self.sextic)
for angleDict in angleList:
angle = angleDict['angle']
isConstrained = angleDict['isConstrained']
......@@ -1739,13 +1739,13 @@ class AmoebaAngleGenerator:
force.addAngle(angle[0], angle[1], angle[2], angle[3], self.angle[i][0], self.k[i])
break
if (hit == 0):
if (hit == 0):
outputString = "AmoebaInPlaneAngleGenerator missing types: [%s %s %s] atoms: " % (type1, type2, type3)
outputString += getAtomPrint( data, angle[0] ) + ' '
outputString += getAtomPrint( data, angle[1] ) + ' '
outputString += getAtomPrint( data, angle[2] )
outputString += " indices: [%6d %6d %6d]" % (angle[0], angle[1], angle[2])
raise ValueError(outputString)
raise ValueError(outputString)
parsers["AmoebaAngleForce"] = AmoebaAngleGenerator.parseElement
......@@ -1761,7 +1761,7 @@ class AmoebaOutOfPlaneBendGenerator:
#=============================================================================================
"""An AmoebaOutOfPlaneBendGenerator constructs a AmoebaOutOfPlaneBendForce."""
#=============================================================================================
def __init__(self, forceField, type, cubic, quartic, pentic, sextic):
......@@ -1784,7 +1784,7 @@ class AmoebaOutOfPlaneBendGenerator:
# Local version of findAtomTypes needed since class indices are 0 (i.e., not recognized)
# for types3 and 4
#=============================================================================================
def findAtomTypes(self, forceField, node, num):
"""Parse the attributes on an XML tag to find the set of atom types for each atom it involves."""
types = []
......@@ -1810,7 +1810,7 @@ class AmoebaOutOfPlaneBendGenerator:
# <AmoebaOutOfPlaneBendForce type="ALLINGER" opbend-cubic="-0.014" opbend-quartic="5.6e-05" opbend-pentic="-7e-07" opbend-sextic="2.2e-08">
# <Angle class1="2" class2="1" class3="0" class4="0" k="0.0531474541591"/>
# <Angle class1="3" class2="1" class3="0" class4="0" k="0.0898536095496"/>
# get global scalar parameters
generator = AmoebaOutOfPlaneBendGenerator(forceField, element.attrib['type'],
......@@ -1835,21 +1835,21 @@ class AmoebaOutOfPlaneBendGenerator:
else:
outputString = "AmoebaOutOfPlaneBendGenerator error getting types: %s %s %s %s." % (
angle.attrib['class1'], angle.attrib['class2'], angle.attrib['class3'], angle.attrib['class4'])
raise ValueError(outputString)
raise ValueError(outputString)
#=============================================================================================
# Get middle atom in a angle
# return index of middle atom or -1 if no middle is found
# This method appears not to be needed since the angle[1] entry appears to always
# be the middle atom. However, was unsure if this is guaranteed
#=============================================================================================
def getMiddleAtom(self, angle, data):
# find atom shared by both bonds making up the angle
middleAtom = -1
for atomIndex in angle:
for atomIndex in angle:
isMiddle = 0
for bond in data.atomBonds[atomIndex]:
atom1 = data.bonds[bond].atom1
......@@ -1858,7 +1858,7 @@ class AmoebaOutOfPlaneBendGenerator:
partner = atom1
else:
partner = atom2
if (partner == angle[0] or partner == angle[1] or partner == angle[2]):
if (partner == angle[0] or partner == angle[1] or partner == angle[2]):
isMiddle += 1
if (isMiddle == 2):
......@@ -1909,10 +1909,10 @@ class AmoebaOutOfPlaneBendGenerator:
middleType = -1
middleCovalency = -1
# if middle atom has covalency of 3 and
# if middle atom has covalency of 3 and
# the types of the middle atom and the partner atom (atom bonded to
# middle atom, but not in angle) match types1 and types2, then
# three out-of-plane bend angles are generated. Three in-plane angle
# three out-of-plane bend angles are generated. Three in-plane angle
# are also generated. If the conditions are not satisfied, the angle is marked as 'generic' angle (not a in-plane angle)
if (middleAtom > -1 and middleCovalency == 3 and middleAtom not in skipAtoms):
......@@ -1939,7 +1939,7 @@ class AmoebaOutOfPlaneBendGenerator:
partnerSet.add(partner)
partnerTypes.append(partnerType)
partnerK.append(self.ks[i])
if (len(partners) == 3):
force.addOutOfPlaneBend(partners[0], middleAtom, partners[1], partners[2], partnerK[2])
......@@ -1984,7 +1984,7 @@ class AmoebaOutOfPlaneBendGenerator:
if (middleAtom > -1 and middleCovalency == 3 and middleAtom in skipAtoms):
partnerSet = skipAtoms[middleAtom]
angleDict = {}
angleList = []
......@@ -2015,12 +2015,12 @@ class AmoebaOutOfPlaneBendGenerator:
# get AmoebaAngleGenerator and add AmoebaAngle and AmoebaInPlaneAngle forces
for force in self.forceField._forces:
if (force.__class__.__name__ == 'AmoebaAngleGenerator'):
if (force.__class__.__name__ == 'AmoebaAngleGenerator'):
force.createForcePostOpBendAngle(sys, data, nonbondedMethod, nonbondedCutoff, nonInPlaneAngles, args)
force.createForcePostOpBendInPlaneAngle(sys, data, nonbondedMethod, nonbondedCutoff, inPlaneAngles, args)
for force in self.forceField._forces:
if (force.__class__.__name__ == 'AmoebaStretchBendGenerator'):
if (force.__class__.__name__ == 'AmoebaStretchBendGenerator'):
for angleDict in inPlaneAngles:
nonInPlaneAngles.append(angleDict)
force.createForcePostAmoebaBondForce(sys, data, nonbondedMethod, nonbondedCutoff, nonInPlaneAngles, args)
......@@ -2048,7 +2048,7 @@ class AmoebaTorsionGenerator:
self.t1 = []
self.t2 = []
self.t3 = []
#=============================================================================================
@staticmethod
......@@ -2057,7 +2057,7 @@ class AmoebaTorsionGenerator:
# <AmoebaTorsionForce torsionUnit="0.5">
# <Torsion class1="3" class2="1" class3="2" class4="3" amp1="0.0" angle1="0.0" amp2="0.0" angle2="3.14159265359" amp3="0.0" angle3="0.0" />
# <Torsion class1="3" class2="1" class3="2" class4="6" amp1="0.0" angle1="0.0" amp2="0.0" angle2="3.14159265359" amp3="-0.263592" angle3="0.0" />
generator = AmoebaTorsionGenerator(float(element.attrib['torsionUnit']))
forceField._forces.append(generator)
......@@ -2095,8 +2095,8 @@ class AmoebaTorsionGenerator:
stretchBend.attrib['class2'],
stretchBend.attrib['class3'],
stretchBend.attrib['class4'])
raise ValueError(outputString)
raise ValueError(outputString)
#=============================================================================================
def createForce(self, sys, data, nontorsionedMethod, nontorsionedCutoff, args):
......@@ -2136,14 +2136,14 @@ class AmoebaTorsionGenerator:
force.addTorsion(torsion[0], torsion[1], torsion[2], torsion[3], 3, self.t3[i][1], self.t3[i][0])
break
if (hit == 0):
if (hit == 0):
outputString = "AmoebaTorsionGenerator missing type: [%s %s %s %s] atoms: " % (type1, type2, type3, type4)
outputString += getAtomPrint( data, torsion[0] ) + ' '
outputString += getAtomPrint( data, torsion[1] ) + ' '
outputString += getAtomPrint( data, torsion[2] ) + ' '
outputString += getAtomPrint( data, torsion[3] )
outputString += " indices: [%6d %6d %6d %6d]" % (torsion[0], torsion[1], torsion[2], torsion[3])
raise ValueError(outputString)
raise ValueError(outputString)
parsers["AmoebaTorsionForce"] = AmoebaTorsionGenerator.parseElement
......@@ -2157,13 +2157,13 @@ class AmoebaPiTorsionGenerator:
"""An AmoebaPiTorsionGenerator constructs a AmoebaPiTorsionForce."""
#=============================================================================================
def __init__(self, piTorsionUnit):
self.piTorsionUnit = piTorsionUnit
self.piTorsionUnit = piTorsionUnit
self.types1 = []
self.types2 = []
self.k = []
#=============================================================================================
@staticmethod
......@@ -2185,8 +2185,8 @@ class AmoebaPiTorsionGenerator:
outputString = "AmoebaPiTorsionGenerator: error getting types: %s %s " % (
piTorsion.attrib['class1'],
piTorsion.attrib['class2'])
raise ValueError(outputString)
raise ValueError(outputString)
#=============================================================================================
def createForce(self, sys, data, nonpiTorsionedMethod, nonpiTorsionedCutoff, args):
......@@ -2203,10 +2203,10 @@ class AmoebaPiTorsionGenerator:
for bond in data.bonds:
# search for bonds with both atoms in bond having covalency == 3
atom1 = bond.atom1
atom2 = bond.atom2
if (len(data.atomBonds[atom1]) == 3 and len(data.atomBonds[atom1]) == 3):
type1 = data.atomType[data.atoms[atom1]]
......@@ -2219,8 +2219,8 @@ class AmoebaPiTorsionGenerator:
if (type1 in types1 and type2 in types2) or (type1 in types2 and type2 in types1):
# piTorsionAtom1, piTorsionAtom2 are the atoms bonded to atom1, excluding atom2
# piTorsionAtom5, piTorsionAtom6 are the atoms bonded to atom2, excluding atom1
# piTorsionAtom1, piTorsionAtom2 are the atoms bonded to atom1, excluding atom2
# piTorsionAtom5, piTorsionAtom6 are the atoms bonded to atom2, excluding atom1
piTorsionAtom1 = -1
piTorsionAtom2 = -1
......@@ -2239,7 +2239,7 @@ class AmoebaPiTorsionGenerator:
b1 = bondedAtom2
if (b1 != atom2):
if (piTorsionAtom1 == -1):
piTorsionAtom1 = b1
piTorsionAtom1 = b1
else:
piTorsionAtom2 = b1
......@@ -2253,10 +2253,10 @@ class AmoebaPiTorsionGenerator:
if (b1 != atom1):
if (piTorsionAtom5 == -1):
piTorsionAtom5 = b1
piTorsionAtom5 = b1
else:
piTorsionAtom6 = b1
force.addPiTorsion(piTorsionAtom1, piTorsionAtom2, piTorsionAtom3, piTorsionAtom4, piTorsionAtom5, piTorsionAtom6, self.k[i])
parsers["AmoebaPiTorsionForce"] = AmoebaPiTorsionGenerator.parseElement
......@@ -2281,7 +2281,7 @@ class AmoebaTorsionTorsionGenerator:
self.gridIndex = []
self.grids = []
#=============================================================================================
@staticmethod
......@@ -2316,8 +2316,8 @@ class AmoebaTorsionTorsionGenerator:
torsionTorsion.attrib['class3'],
torsionTorsion.attrib['class4'],
torsionTorsion.attrib['class5'] )
raise ValueError(outputString)
raise ValueError(outputString)
# load grid
# xml source
......@@ -2328,12 +2328,12 @@ class AmoebaTorsionTorsionGenerator:
# output grid:
# grid[x][y][0] = x value
# grid[x][y][1] = y value
# grid[x][y][2] = function value
# grid[x][y][3] = dfdx value
# grid[x][y][4] = dfdy value
# grid[x][y][5] = dfd(xy) value
# grid[x][y][0] = x value
# grid[x][y][1] = y value
# grid[x][y][2] = function value
# grid[x][y][3] = dfdx value
# grid[x][y][4] = dfdy value
# grid[x][y][5] = dfd(xy) value
maxGridIndex += 1
generator.grids = maxGridIndex*[]
......@@ -2365,7 +2365,7 @@ class AmoebaTorsionTorsionGenerator:
gridCol = []
gridColIndex = 0
if (gridIndex == len(generator.grids)):
generator.grids.append(grid)
else:
......@@ -2401,33 +2401,33 @@ class AmoebaTorsionTorsionGenerator:
atomE = hit
else:
atomF = hit
# raise error if atoms E or F not found
if (atomE == -1 or atomF == -1):
outputString = "getChiralAtomIndex: error getting bonded partners of atomC=%s %d %s" % (atomC.name, atomC.resiude.index, atomC.resiude.name,)
raise ValueError(outputString)
raise ValueError(outputString)
# check for different type/mass between atoms E & F
typeE = int(data.atomType[data.atoms[atomE]])
typeF = int(data.atomType[data.atoms[atomF]])
if (typeE > typeF):
chiralAtomIndex = atomE
chiralAtomIndex = atomE
if (typeF > typeE):
chiralAtomIndex = atomF
chiralAtomIndex = atomF
massE = sys.getParticleMass(atomE)/unit.dalton
massF = sys.getParticleMass(atomE)/unit.dalton
if (massE > massF):
chiralAtomIndex = massE
chiralAtomIndex = massE
if (massF > massE):
chiralAtomIndex = massF
chiralAtomIndex = massF
return chiralAtomIndex
#=============================================================================================
def createForce(self, sys, data, nonpiTorsionedMethod, nonpiTorsionedCutoff, args):
existing = [sys.getForce(i) for i in range(sys.getNumForces())]
......@@ -2441,8 +2441,8 @@ class AmoebaTorsionTorsionGenerator:
for angle in data.angles:
# search for bitorsions; based on TINKER subroutine bitors()
# search for bitorsions; based on TINKER subroutine bitors()
ib = angle[0]
ic = angle[1]
id = angle[2]
......@@ -2499,7 +2499,7 @@ class AmoebaTorsionTorsionGenerator:
for (index, grid) in enumerate(self.grids):
force.setTorsionTorsionGrid(index, grid)
parsers["AmoebaTorsionTorsionForce"] = AmoebaTorsionTorsionGenerator.parseElement
#=============================================================================================
......@@ -2519,7 +2519,7 @@ class AmoebaStretchBendGenerator:
self.k1 = []
self.k2 = []
#=============================================================================================
@staticmethod
......@@ -2547,15 +2547,15 @@ class AmoebaStretchBendGenerator:
stretchBend.attrib['class1'],
stretchBend.attrib['class2'],
stretchBend.attrib['class3'])
raise ValueError(outputString)
raise ValueError(outputString)
#=============================================================================================
# The setup of this force is dependent on AmoebaBondForce and AmoebaAngleForce
# The setup of this force is dependent on AmoebaBondForce and AmoebaAngleForce
# having been called since the ideal bond lengths and angle are needed here.
# As a conseqeunce, createForce() is not implemented since it is not guaranteed that the generator for
# AmoebaBondForce and AmoebaAngleForce have been called prior to AmoebaStretchBendGenerator().
# Instead, createForcePostAmoebaBondForce() is called
# AmoebaBondForce and AmoebaAngleForce have been called prior to AmoebaStretchBendGenerator().
# Instead, createForcePostAmoebaBondForce() is called
# after the generators for AmoebaBondForce and AmoebaAngleForce have been called
#=============================================================================================
......@@ -2602,7 +2602,7 @@ class AmoebaStretchBendGenerator:
# get ideal bond lengths, bondAB, bondCB
# get ideal angle
if (type2 in types2 and ((type1 in types1 and type3 in types3) or (type3 in types1 and type1 in types3))):
if (type2 in types2 and ((type1 in types1 and type3 in types3) or (type3 in types1 and type1 in types3))):
bondAB = -1.0
bondCB = -1.0
swap = 0
......@@ -2618,7 +2618,7 @@ class AmoebaStretchBendGenerator:
bondCB = length
if (atom2 == angle[0]):
bondAB = length
# check that ideal angle and bonds are set
if ('idealAngle' not in angleDict):
......@@ -2628,7 +2628,7 @@ class AmoebaStretchBendGenerator:
outputString += getAtomPrint( data, angle[0] ) + ' '
outputString += getAtomPrint( data, angle[1] ) + ' '
outputString += getAtomPrint( data, angle[2] )
raise ValueError(outputString)
raise ValueError(outputString)
elif (bondAB < 0 or bondCB < 0):
......@@ -2637,7 +2637,7 @@ class AmoebaStretchBendGenerator:
outputString += getAtomPrint( data, angle[0] ) + ' '
outputString += getAtomPrint( data, angle[1] ) + ' '
outputString += getAtomPrint( data, angle[2] )
raise ValueError(outputString)
raise ValueError(outputString)
else:
force.addStretchBend(angle[0], angle[1], angle[2], bondAB, bondCB, angleDict['idealAngle']/radian, self.k1[i])
......@@ -2652,12 +2652,12 @@ parsers["AmoebaStretchBendForce"] = AmoebaStretchBendGenerator.parseElement
class AmoebaVdwGenerator:
"""A AmoebaVdwGenerator constructs a AmoebaVdwForce."""
#=============================================================================================
def __init__(self, type, radiusrule, radiustype, radiussize, epsilonrule, vdw13Scale, vdw14Scale, vdw15Scale):
self.type = type
self.type = type
self.radiusrule = radiusrule
self.radiustype = radiustype
......@@ -2677,11 +2677,11 @@ class AmoebaVdwGenerator:
def parseElement(element, forceField):
# <AmoebaVdwForce type="BUFFERED-14-7" radiusrule="CUBIC-MEAN" radiustype="R-MIN" radiussize="DIAMETER" epsilonrule="HHG" vdw-13-scale="0.0" vdw-14-scale="1.0" vdw-15-scale="1.0" >
# <Vdw class="1" sigma="0.371" epsilon="0.46024" reduction="1.0" />
# <Vdw class="2" sigma="0.382" epsilon="0.422584" reduction="1.0" />
generator = AmoebaVdwGenerator(element.attrib['type'], element.attrib['radiusrule'], element.attrib['radiustype'], element.attrib['radiussize'], element.attrib['epsilonrule'],
float(element.attrib['vdw-13-scale']), float(element.attrib['vdw-14-scale']), float(element.attrib['vdw-15-scale']))
# <Vdw class="1" sigma="0.371" epsilon="0.46024" reduction="1.0" />
# <Vdw class="2" sigma="0.382" epsilon="0.422584" reduction="1.0" />
generator = AmoebaVdwGenerator(element.attrib['type'], element.attrib['radiusrule'], element.attrib['radiustype'], element.attrib['radiussize'], element.attrib['epsilonrule'],
float(element.attrib['vdw-13-scale']), float(element.attrib['vdw-14-scale']), float(element.attrib['vdw-15-scale']))
forceField._forces.append(generator)
two_six = 1.122462048309372
......@@ -2697,17 +2697,17 @@ class AmoebaVdwGenerator:
if (generator.radiustype == 'SIGMA'):
values[0] *= two_six
if (generator.radiussize == 'DIAMETER'):
values[0] *= 0.5
for t in types[0]:
generator.typeMap[t] = values
else:
outputString = "AmoebaVdwGenerator: error getting type: %s" % (atom.attrib['class'])
raise ValueError(outputString)
raise ValueError(outputString)
#=============================================================================================
# Return a set containing the indices of particles bonded to particle with index=particleIndex
......@@ -2728,7 +2728,7 @@ class AmoebaVdwGenerator:
bondedParticleSet.add(atom2)
return bondedParticleSet
#=============================================================================================
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
......@@ -2762,10 +2762,10 @@ class AmoebaVdwGenerator:
force.setEpsilonCombiningRule(epsilonRule.upper())
else:
stringList = ' ' . join(str(x) for x in epsilonMap.keys())
raise ValueError( "AmoebaVdwGenerator: epsilon combining rule %s not recognized; valid values are %s; using default." % (epsilonRule, stringList) )
raise ValueError( "AmoebaVdwGenerator: epsilon combining rule %s not recognized; valid values are %s; using default." % (epsilonRule, stringList) )
else:
force.setEpsilonCombiningRule(self.epsilonrule)
# cutoff
if ('vdwCutoff' in args):
......@@ -2780,19 +2780,19 @@ class AmoebaVdwGenerator:
if (nonbondedMethod == PME):
force.setNonbondedMethod(mm.AmoebaVdwForce.CutoffPeriodic)
else:
force = existing[0]
# add particles to force
# throw error if particle type not available
# throw error if particle type not available
for (i, atom) in enumerate(data.atoms):
t = data.atomType[atom]
if t in self.typeMap:
values = self.typeMap[t]
# ivIndex = index of bonded partner for hydrogens; otherwise ivIndex = particle index
ivIndex = i
......@@ -2819,7 +2819,7 @@ class AmoebaVdwGenerator:
bondedParticleSets.append(AmoebaVdwGenerator.getBondedParticleSet(i, data))
for (i,atom) in enumerate(data.atoms):
# 1-2 partners
exclusionSet = bondedParticleSets[i].copy()
......@@ -2846,7 +2846,7 @@ class AmoebaMultipoleGenerator:
#=============================================================================================
"""A AmoebaMultipoleGenerator constructs a AmoebaMultipoleForce."""
#=============================================================================================
def __init__(self, forceField,
......@@ -2857,25 +2857,25 @@ class AmoebaMultipoleGenerator:
self.forceField = forceField
self.direct11Scale = direct11Scale
self.direct12Scale = direct12Scale
self.direct13Scale = direct13Scale
self.direct14Scale = direct14Scale
self.direct11Scale = direct11Scale
self.direct12Scale = direct12Scale
self.direct13Scale = direct13Scale
self.direct14Scale = direct14Scale
self.mpole12Scale = mpole12Scale
self.mpole13Scale = mpole13Scale
self.mpole14Scale = mpole14Scale
self.mpole15Scale = mpole15Scale
self.mpole12Scale = mpole12Scale
self.mpole13Scale = mpole13Scale
self.mpole14Scale = mpole14Scale
self.mpole15Scale = mpole15Scale
self.mutual11Scale = mutual11Scale
self.mutual12Scale = mutual12Scale
self.mutual13Scale = mutual13Scale
self.mutual14Scale = mutual14Scale
self.mutual11Scale = mutual11Scale
self.mutual12Scale = mutual12Scale
self.mutual13Scale = mutual13Scale
self.mutual14Scale = mutual14Scale
self.polar12Scale = polar12Scale
self.polar13Scale = polar13Scale
self.polar14Scale = polar14Scale
self.polar15Scale = polar15Scale
self.polar12Scale = polar12Scale
self.polar13Scale = polar13Scale
self.polar14Scale = polar14Scale
self.polar15Scale = polar15Scale
self.typeMap = {}
......@@ -2893,12 +2893,12 @@ class AmoebaMultipoleGenerator:
ky = kIndices[3]
else:
ky = 0
if (kIndicesLen > 2):
kx = kIndices[2]
else:
kx = 0
if (kIndicesLen > 1):
kz = kIndices[1]
else:
......@@ -2919,9 +2919,9 @@ class AmoebaMultipoleGenerator:
if (kz < 0 and kx < 0 and ky < 0):
axisType = mm.AmoebaMultipoleForce.ThreeFold
kIndices[1] = abs(kz)
kIndices[2] = abs(kx)
kIndices[3] = abs(ky)
kIndices[1] = abs(kz)
kIndices[2] = abs(kx)
kIndices[3] = abs(ky)
return axisType
......@@ -2930,7 +2930,7 @@ class AmoebaMultipoleGenerator:
@staticmethod
def parseElement(element, forceField):
# <AmoebaMultipoleForce direct11Scale="0.0" direct12Scale="1.0" direct13Scale="1.0" direct14Scale="1.0" mpole12Scale="0.0" mpole13Scale="0.0" mpole14Scale="0.4" mpole15Scale="0.8" mutual11Scale="1.0" mutual12Scale="1.0" mutual13Scale="1.0" mutual14Scale="1.0" polar12Scale="0.0" polar13Scale="0.0" polar14Intra="0.5" polar14Scale="1.0" polar15Scale="1.0" >
# <AmoebaMultipoleForce direct11Scale="0.0" direct12Scale="1.0" direct13Scale="1.0" direct14Scale="1.0" mpole12Scale="0.0" mpole13Scale="0.0" mpole14Scale="0.4" mpole15Scale="0.8" mutual11Scale="1.0" mutual12Scale="1.0" mutual13Scale="1.0" mutual14Scale="1.0" polar12Scale="0.0" polar13Scale="0.0" polar14Intra="0.5" polar14Scale="1.0" polar15Scale="1.0" >
# <Multipole class="1" kz="2" kx="4" c0="-0.22620" d1="0.08214" d2="0.00000" d3="0.34883" q11="0.11775" q21="0.00000" q22="-1.02185" q31="-0.17555" q32="0.00000" q33="0.90410" />
# <Multipole class="2" kz="1" kx="3" c0="-0.15245" d1="0.19517" d2="0.00000" d3="0.19687" q11="-0.20677" q21="0.00000" q22="-0.48084" q31="-0.01672" q32="0.00000" q33="0.68761" />
......@@ -2974,17 +2974,17 @@ class AmoebaMultipoleGenerator:
try:
if (atom.attrib[kString]):
kIndices.append(int(atom.attrib[kString]))
except:
except:
pass
# set axis type based on k-Indices
# set axis type based on k-Indices
axisType = AmoebaMultipoleGenerator.setAxisType(kIndices)
# set multipole
charge = float(atom.attrib['c0'])
conversion = 1.0
dipole = [ conversion*float(atom.attrib['d1']), conversion*float(atom.attrib['d2']), conversion*float(atom.attrib['d3'])]
......@@ -3006,18 +3006,18 @@ class AmoebaMultipoleGenerator:
valueMap = dict()
valueMap['classIndex'] = atom.attrib['type']
valueMap['kIndices'] = kIndices
valueMap['charge'] = charge
valueMap['charge'] = charge
valueMap['dipole'] = dipole
valueMap['quadrupole'] = quadrupole
valueMap['axisType'] = axisType
generator.typeMap[t].append(valueMap)
else:
outputString = "AmoebaMultipoleGenerator: error getting type for multipole: %s" % (atom.attrib['class'])
raise ValueError(outputString)
raise ValueError(outputString)
# polarization parameters
for atom in element.findall('Polarize'):
types = forceField._findAtomTypes(atom, 1)
if None not in types:
......@@ -3039,7 +3039,7 @@ class AmoebaMultipoleGenerator:
for t in types[0]:
if (t not in generator.typeMap):
outputString = "AmoebaMultipoleGenerator: polarize type not present: %s" % (atom.attrib['type'])
raise ValueError(outputString)
raise ValueError(outputString)
else:
typeMapList = generator.typeMap[t]
hit = 0
......@@ -3049,18 +3049,18 @@ class AmoebaMultipoleGenerator:
typeMap['polarizability'] = polarizability
typeMap['thole'] = thole
typeMap['pdamp'] = pdamp
typeMap['pgrpMap'] = pgrpMap
typeMap['pgrpMap'] = pgrpMap
typeMapList[ii] = typeMap
hit = 1
if (hit == 0):
outputString = "AmoebaMultipoleGenerator: error getting type for polarize: class index=%s not in multipole list?" % (atom.attrib['class'])
raise ValueError(outputString)
raise ValueError(outputString)
else:
outputString = "AmoebaMultipoleGenerator: error getting type for polarize: %s" % (atom.attrib['class'])
raise ValueError(outputString)
raise ValueError(outputString)
#=============================================================================================
def setPolarGroups(self, data, bonded12ParticleSets, force):
......@@ -3081,7 +3081,7 @@ class AmoebaMultipoleGenerator:
if (bondedAtomType in pgrpMap):
atom.polarizationGroups[bondedAtomIndex] = 1
bondedAtom.polarizationGroups[atomIndex] = 1
# pgrp11
for (atomIndex, atom) in enumerate(data.atoms):
......@@ -3128,7 +3128,7 @@ class AmoebaMultipoleGenerator:
pgrp12 = pgrp12 - pgrp11
for pgrpAtomIndex in pgrp11:
data.atoms[pgrpAtomIndex].polarizationGroupSet.append(pgrp12)
for (atomIndex, atom) in enumerate(data.atoms):
atom.polarizationGroupSet[1] = sorted(atom.polarizationGroupSet[1])
force.setCovalentMap(atomIndex, mm.AmoebaMultipoleForce.PolarizationCovalent12, atom.polarizationGroupSet[1])
......@@ -3150,7 +3150,7 @@ class AmoebaMultipoleGenerator:
pgrp13 = pgrp13 - set(pgrp11)
for pgrpAtomIndex in pgrp11:
data.atoms[pgrpAtomIndex].polarizationGroupSet.append(pgrp13)
for (atomIndex, atom) in enumerate(data.atoms):
atom.polarizationGroupSet[2] = sorted(atom.polarizationGroupSet[2])
force.setCovalentMap(atomIndex, mm.AmoebaMultipoleForce.PolarizationCovalent13, atom.polarizationGroupSet[2])
......@@ -3176,7 +3176,7 @@ class AmoebaMultipoleGenerator:
for pgrpAtomIndex in pgrp11:
data.atoms[pgrpAtomIndex].polarizationGroupSet.append(pgrp14)
for (atomIndex, atom) in enumerate(data.atoms):
atom.polarizationGroupSet[3] = sorted(atom.polarizationGroupSet[3])
force.setCovalentMap(atomIndex, mm.AmoebaMultipoleForce.PolarizationCovalent14, atom.polarizationGroupSet[3])
......@@ -3195,7 +3195,7 @@ class AmoebaMultipoleGenerator:
if len(existing) == 0:
force = mm.AmoebaMultipoleForce()
sys.addForce(force)
if (nonbondedMethod not in methodMap):
if (nonbondedMethod not in methodMap):
raise ValueError( "AmoebaMultipoleForce: input cutoff method not available." )
else:
force.setNonbondedMethod(methodMap[nonbondedMethod])
......@@ -3227,7 +3227,7 @@ class AmoebaMultipoleGenerator:
force = existing[0]
# add particles to force
# throw error if particle type not available
# throw error if particle type not available
# get 1-2, 1-3, 1-4, 1-5 bonded sets
......@@ -3245,7 +3245,7 @@ class AmoebaMultipoleGenerator:
for i in range(len(data.atoms)):
bonded13Set = set()
bonded12ParticleSet = bonded12ParticleSets[i]
for j in bonded12ParticleSet:
for j in bonded12ParticleSet:
bonded13Set = bonded13Set.union(bonded12ParticleSets[j])
# remove 1-2 and self from set
......@@ -3263,9 +3263,9 @@ class AmoebaMultipoleGenerator:
for i in range(len(data.atoms)):
bonded14Set = set()
bonded13ParticleSet = bonded13ParticleSets[i]
for j in bonded13ParticleSet:
for j in bonded13ParticleSet:
bonded14Set = bonded14Set.union(bonded12ParticleSets[j])
# remove 1-3, 1-2 and self from set
bonded14Set = bonded14Set - bonded12ParticleSets[i]
......@@ -3282,7 +3282,7 @@ class AmoebaMultipoleGenerator:
for i in range(len(data.atoms)):
bonded15Set = set()
bonded14ParticleSet = bonded14ParticleSets[i]
for j in bonded14ParticleSet:
for j in bonded14ParticleSet:
bonded15Set = bonded15Set.union(bonded12ParticleSets[j])
# remove 1-4, 1-3, 1-2 and self from set
......@@ -3312,15 +3312,15 @@ class AmoebaMultipoleGenerator:
break
kIndices = multipoleDict['kIndices']
kz = kIndices[1]
kz = kIndices[1]
kx = kIndices[2]
ky = kIndices[3]
# assign multipole parameters
# (1) get bonded partners
# (2) match parameter types
bondedAtomIndices = bonded12ParticleSets[atomIndex]
zaxis = -1
xaxis = -1
......@@ -3364,7 +3364,7 @@ class AmoebaMultipoleGenerator:
yaxis = bondedAtomYIndex
savedMultipoleDict = multipoleDict
hit = 2
# assign multipole parameters via 1-2 and 1-3 connected atoms
for multipoleDict in multipoleList:
......@@ -3373,15 +3373,15 @@ class AmoebaMultipoleGenerator:
break
kIndices = multipoleDict['kIndices']
kz = kIndices[1]
kz = kIndices[1]
kx = kIndices[2]
ky = kIndices[3]
# assign multipole parameters
# (1) get bonded partners
# (2) match parameter types
bondedAtom12Indices = bonded12ParticleSets[atomIndex]
bondedAtom13Indices = bonded13ParticleSets[atomIndex]
......@@ -3428,7 +3428,7 @@ class AmoebaMultipoleGenerator:
yaxis = bondedAtomYIndex
savedMultipoleDict = multipoleDict
hit = 4
# assign multipole parameters via only a z-defining atom
for multipoleDict in multipoleList:
......@@ -3437,10 +3437,10 @@ class AmoebaMultipoleGenerator:
break
kIndices = multipoleDict['kIndices']
kz = kIndices[1]
kx = kIndices[2]
kz = kIndices[1]
kx = kIndices[2]
zaxis = -1
xaxis = -1
yaxis = -1
......@@ -3466,9 +3466,9 @@ class AmoebaMultipoleGenerator:
break
kIndices = multipoleDict['kIndices']
kz = kIndices[1]
kz = kIndices[1]
zaxis = -1
xaxis = -1
yaxis = -1
......@@ -3476,7 +3476,7 @@ class AmoebaMultipoleGenerator:
if (kz == 0):
savedMultipoleDict = multipoleDict
hit = 6
# add particle if there was a hit
if (hit != 0):
......@@ -3509,19 +3509,19 @@ parsers["AmoebaMultipoleForce"] = AmoebaMultipoleGenerator.parseElement
class AmoebaWcaDispersionGenerator:
"""A AmoebaWcaDispersionGenerator constructs a AmoebaWcaDispersionForce."""
#=========================================================================================
def __init__(self, epso, epsh, rmino, rminh, awater, slevy, dispoff, shctd):
self.epso = epso
self.epsh = epsh
self.epso = epso
self.epsh = epsh
self.rmino = rmino
self.rminh = rminh
self.awater = awater
self.slevy = slevy
self.dispoff = dispoff
self.shctd = shctd
self.shctd = shctd
self.typeMap = {}
......@@ -3533,15 +3533,15 @@ class AmoebaWcaDispersionGenerator:
# <AmoebaWcaDispersionForce epso="0.46024" epsh="0.056484" rmino="0.17025" rminh="0.13275" awater="33.428" slevy="1.0" dispoff="0.026" shctd="0.81" >
# <WcaDispersion class="1" radius="0.1855" epsilon="0.46024" />
# <WcaDispersion class="2" radius="0.191" epsilon="0.422584" />
generator = AmoebaWcaDispersionGenerator(element.attrib['epso'],
element.attrib['epsh'],
element.attrib['rmino'],
element.attrib['rminh'],
element.attrib['awater'],
element.attrib['awater'],
element.attrib['slevy'],
element.attrib['dispoff'],
element.attrib['shctd'])
element.attrib['shctd'])
forceField._forces.append(generator)
# typeMap[] = [ radius, epsilon ]
......@@ -3555,10 +3555,10 @@ class AmoebaWcaDispersionGenerator:
generator.typeMap[t] = values
else:
outputString = "AmoebaWcaDispersionGenerator: error getting type: %s" % (atom.attrib['class'])
raise ValueError(outputString)
raise ValueError(outputString)
#=========================================================================================
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
# get or create force depending on whether it has already been added to the system
......@@ -3572,7 +3572,7 @@ class AmoebaWcaDispersionGenerator:
force = existing[0]
# add particles to force
# throw error if particle type not available
# throw error if particle type not available
force.setEpso( float(self.epso ))
force.setEpsh( float(self.epsh ))
......@@ -3600,7 +3600,7 @@ parsers["AmoebaWcaDispersionForce"] = AmoebaWcaDispersionGenerator.parseElement
class AmoebaGeneralizedKirkwoodGenerator:
"""A AmoebaGeneralizedKirkwoodGenerator constructs a AmoebaGeneralizedKirkwoodForce."""
#=========================================================================================
def __init__(self, forceField, solventDielectric, soluteDielectric, includeCavityTerm, probeRadius, surfaceAreaFactor):
......@@ -3614,7 +3614,7 @@ class AmoebaGeneralizedKirkwoodGenerator:
self.radiusTypeMap = {}
self.radiusTypeMap['Bondi'] = {}
bondiMap = self.radiusTypeMap['Bondi']
bondiMap = self.radiusTypeMap['Bondi']
rscale = 1.03
bondiMap[0] = 0.00
......@@ -3650,28 +3650,28 @@ class AmoebaGeneralizedKirkwoodGenerator:
shct = -1.0
# shct
if (atomicNumber == 1): # H(1)
shct = 0.85
shct = 0.85
elif (atomicNumber == 6): # C(6)
shct = 0.72
shct = 0.72
elif (atomicNumber == 7): # N(7)
shct = 0.79
shct = 0.79
elif (atomicNumber == 8): # O(8)
shct = 0.85
shct = 0.85
elif (atomicNumber == 9): # F(9)
shct = 0.88
elif (atomicNumber == 15): # P(15)
shct = 0.86
shct = 0.88
elif (atomicNumber == 15): # P(15)
shct = 0.86
elif (atomicNumber == 16): # S(16)
shct = 0.96
elif (atomicNumber == 26): # Fe(26)
shct = 0.88
if (shct < 0.0):
if (shct < 0.0):
raise ValueError( "getObcShct: no GK overlap scale factor for atom %s of %s %d" % (atom.name, atom.residue.name, atom.residue.index) )
return shct
return shct
#=========================================================================================
......@@ -3682,12 +3682,12 @@ class AmoebaGeneralizedKirkwoodGenerator:
radius = -1.0
if (atomicNumber == 1): # H(1)
radius = 0.132
if (len(bondedAtomIndices) < 1):
raise ValueError( "AmoebaGeneralizedKirkwoodGenerator: error getting atom bonded to %s of %s %d " % (atom.name, atom.residue.name, atom.residue.index) )
for bondedAtomIndex in bondedAtomIndices:
bondedAtomAtomicNumber = data.atoms[bondedAtomIndex].element.atomic_number
......@@ -3695,11 +3695,11 @@ class AmoebaGeneralizedKirkwoodGenerator:
radius = 0.11
if (bondedAtomAtomicNumber == 8):
radius = 0.105
elif (atomicNumber == 3): # Li(3)
radius = 0.15
elif (atomicNumber == 6): # C(6)
radius = 0.20
if (len(bondedAtomIndices) == 3):
radius = 0.205
......@@ -3718,60 +3718,60 @@ class AmoebaGeneralizedKirkwoodGenerator:
radius = 0.145
elif (atomicNumber == 9): # F(9)
radius = 0.154
elif (atomicNumber == 10):
elif (atomicNumber == 10):
radius = 0.146
elif (atomicNumber == 11):
elif (atomicNumber == 11):
radius = 0.209
elif (atomicNumber == 12):
elif (atomicNumber == 12):
radius = 0.179
elif (atomicNumber == 14):
elif (atomicNumber == 14):
radius = 0.189
elif (atomicNumber == 15): # P(15)
elif (atomicNumber == 15): # P(15)
radius = 0.196
elif (atomicNumber == 16): # S(16)
radius = 0.186
elif (atomicNumber == 17):
elif (atomicNumber == 17):
radius = 0.182
elif (atomicNumber == 18):
elif (atomicNumber == 18):
radius = 0.179
elif (atomicNumber == 19):
elif (atomicNumber == 19):
radius = 0.223
elif (atomicNumber == 20):
elif (atomicNumber == 20):
radius = 0.191
elif (atomicNumber == 35):
elif (atomicNumber == 35):
radius = 2.00
elif (atomicNumber == 36):
elif (atomicNumber == 36):
radius = 0.190
elif (atomicNumber == 37):
elif (atomicNumber == 37):
radius = 0.226
elif (atomicNumber == 53):
elif (atomicNumber == 53):
radius = 0.237
elif (atomicNumber == 54):
elif (atomicNumber == 54):
radius = 0.207
elif (atomicNumber == 55):
elif (atomicNumber == 55):
radius = 0.263
elif (atomicNumber == 56):
elif (atomicNumber == 56):
radius = 0.230
if (radius < 0.0):
if (radius < 0.0):
outputString = "No GK radius for atom %s of %s %d" % (atom.name, atom.residue.name, atom.residue.index)
raise ValueError( outputString )
return radius
#=========================================================================================
def getBondiTypeRadius(self, data, bondedAtomIndices, atomIndex):
bondiMap = self.radiusTypeMap['Bondi']
bondiMap = self.radiusTypeMap['Bondi']
atom = data.atoms[atomIndex]
atomicNumber = atom.element.atomic_number
if (atomicNumber in bondiMap):
if (atomicNumber in bondiMap):
radius = bondiMap[atomicNumber]
else:
outputString = "Warning no Bondi radius for atom %s of %s %d using default value=%f" % (atom.name, atom.residue.name, atom.residue.index, radius)
raise ValueError( outputString )
return radius
#=========================================================================================
......@@ -3782,14 +3782,14 @@ class AmoebaGeneralizedKirkwoodGenerator:
# <AmoebaGeneralizedKirkwoodForce solventDielectric="78.3" soluteDielectric="1.0" includeCavityTerm="1" probeRadius="0.14" surfaceAreaFactor="-170.351730663">
# <GeneralizedKirkwood type="1" charge="-0.22620" shct="0.79" />
# <GeneralizedKirkwood type="2" charge="-0.15245" shct="0.72" />
generator = AmoebaGeneralizedKirkwoodGenerator(forceField, element.attrib['solventDielectric'], element.attrib['soluteDielectric'],
element.attrib['includeCavityTerm'],
element.attrib['probeRadius'], element.attrib['surfaceAreaFactor'])
element.attrib['includeCavityTerm'],
element.attrib['probeRadius'], element.attrib['surfaceAreaFactor'])
forceField._forces.append(generator)
#=========================================================================================
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
if( nonbondedMethod != NoCutoff ):
......@@ -3806,9 +3806,9 @@ class AmoebaGeneralizedKirkwoodGenerator:
# call AmoebaMultipoleForceGenerator.createForce() to ensure charges have been set
for force in self.forceField._forces:
if (force.__class__.__name__ == 'AmoebaMultipoleGenerator'):
if (force.__class__.__name__ == 'AmoebaMultipoleGenerator'):
force.createForce(sys, data, nonbondedMethod, nonbondedCutoff, args)
# get or create force depending on whether it has already been added to the system
existing = [f for f in existing if type(f) == mm.AmoebaGeneralizedKirkwoodForce]
......@@ -3816,7 +3816,7 @@ class AmoebaGeneralizedKirkwoodGenerator:
force = mm.AmoebaGeneralizedKirkwoodForce()
sys.addForce(force)
if ('solventDielectric' in args):
force.setSolventDielectric(float(args['solventDielectric']))
else:
......@@ -3836,7 +3836,7 @@ class AmoebaGeneralizedKirkwoodGenerator:
force = existing[0]
# add particles to force
# throw error if particle type not available
# throw error if particle type not available
force.setProbeRadius( float(self.probeRadius))
force.setSurfaceAreaFactor( float(self.surfaceAreaFactor))
......@@ -3870,7 +3870,7 @@ class AmoebaUreyBradleyGenerator:
#=============================================================================================
"""An AmoebaUreyBradleyGenerator constructs a AmoebaUreyBradleyForce."""
#=============================================================================================
def __init__(self):
self.types1 = []
......@@ -3886,7 +3886,7 @@ class AmoebaUreyBradleyGenerator:
def parseElement(element, forceField):
# <AmoebaUreyBradleyForce>
# <UreyBradley class1="74" class2="73" class3="74" k="16003.8" d="0.15537" />
# <UreyBradley class1="74" class2="73" class3="74" k="16003.8" d="0.15537" />
generator = AmoebaUreyBradleyGenerator()
forceField._forces.append(generator)
......@@ -3904,8 +3904,8 @@ class AmoebaUreyBradleyGenerator:
else:
outputString = "AmoebaUreyBradleyGenerator: error getting types: %s %s %s" % (
bond.attrib['class1'], bond.attrib['class2'], bond.attrib['class3'])
raise ValueError(outputString)
raise ValueError(outputString)
#=============================================================================================
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
......@@ -3941,7 +3941,7 @@ parsers["AmoebaUreyBradleyForce"] = AmoebaUreyBradleyGenerator.parseElement
## @private
class DrudeGenerator:
"""A DrudeGenerator constructs a DrudeForce."""
def __init__(self):
self.typeMap = {}
......@@ -3966,14 +3966,14 @@ class DrudeGenerator:
values = (types[1], types[2], types[3], types[4], float(particle.attrib['charge']), float(particle.attrib['polarizability']), aniso12, aniso34, float(particle.attrib['thole']))
for t in types[0]:
generator.typeMap[t] = values
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
force = mm.DrudeForce()
if not any(isinstance(f, mm.NonbondedForce) for f in sys.getForces()):
raise ValueError('<DrudeForce> must come after <NonbondedForce> in XML file')
# Add Drude particles.
drudeMap = {}
parentMap = {}
for atom in data.atoms:
......@@ -3996,10 +3996,10 @@ class DrudeGenerator:
drudeMap[atom.index] = p[0]
parentMap[p[0]] = (atom.index, drudeIndex)
sys.addForce(force)
def postprocessSystem(self, sys, data, args):
# For every nonbonded exclusion between Drude particles, add a screened pair.
drude = [f for f in sys.getForces() if isinstance(f, mm.DrudeForce)][0]
nonbonded = [f for f in sys.getForces() if isinstance(f, mm.NonbondedForce)][0]
particleMap = {}
......@@ -4019,4 +4019,4 @@ class DrudeGenerator:
thole2 = self.typeMap[type2][8]
drude.addScreenedPair(drude1, drude2, thole1+thole2)
parsers["DrudeForce"] = DrudeGenerator.parseElement
\ No newline at end of file
parsers["DrudeForce"] = DrudeGenerator.parseElement
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012 Stanford University and the Authors.
Authors: Lee-Ping Wang, Peter Eastman
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -47,7 +47,7 @@ def _isint(word):
@param[in] word String (for instance, '123', '153.0', '2.', '-354')
@return answer Boolean which specifies whether the string is an integer (only +/- sign followed by digits)
"""
return match('^[-+]?[0-9]+$',word)
......@@ -57,7 +57,7 @@ def _isfloat(word):
@param[in] word String (for instance, '123', '153.0', '2.', '-354')
@return answer Boolean which specifies whether the string is any number
"""
return match('^[-+]?[0-9]*\.?[0-9]*([eEdD][-+]?[0-9]+)?$',word)
......@@ -65,7 +65,7 @@ def _is_gro_coord(line):
""" Determines whether a line contains GROMACS data or not
@param[in] line The line to be tested
"""
sline = line.split()
if len(sline) == 6 or len(sline) == 9:
......@@ -79,7 +79,7 @@ def _is_gro_box(line):
""" Determines whether a line contains a GROMACS box vector or not
@param[in] line The line to be tested
"""
sline = line.split()
if len(sline) == 9 and all([_isfloat(i) for i in sline]):
......@@ -91,16 +91,16 @@ def _is_gro_box(line):
class GromacsGroFile(object):
"""GromacsGroFile parses a Gromacs .gro file and constructs a set of atom positions from it.
A .gro file also contains some topological information, such as elements and residue names,
but not enough to construct a full Topology object. This information is recorded and stored
in the object's public fields."""
def __init__(self, file):
"""Load a .gro file.
The atom positions can be retrieved by calling getPositions().
Parameters:
- file (string) the name of the file to load
"""
......@@ -145,7 +145,7 @@ class GromacsGroFile(object):
else:
raise Exception("Unexpected line in .gro file: "+line)
ln += 1
## The atom positions read from the file. If the file contains multiple frames, these are the positions in the first frame.
self.positions = xyzs[0]
## A list containing the element of each atom stored in the file
......@@ -159,14 +159,14 @@ class GromacsGroFile(object):
self._positions = xyzs
self._unitCellDimensions = boxes
self._numpyPositions = None
def getNumFrames(self):
"""Get the number of frames stored in the file."""
return len(self._positions)
def getPositions(self, asNumpy=False, frame=0):
"""Get the atomic positions.
Parameters:
- asNumpy (boolean=False) if true, the values are returned as a numpy array instead of a list of Vec3s
- frame (int=0) the index of the frame for which to get positions
......@@ -178,10 +178,10 @@ class GromacsGroFile(object):
self._numpyPositions[frame] = Quantity(numpy.array(self._positions[frame].value_in_unit(nanometers)), nanometers)
return self._numpyPositions[frame]
return self._positions[frame]
def getUnitCellDimensions(self, frame=0):
"""Get the dimensions of the crystallographic unit cell.
Parameters:
- frame (int=0) the index of the frame for which to get the unit cell dimensions
"""
......
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012-2013 Stanford University and the Authors.
Authors: Peter Eastman
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -49,7 +49,7 @@ OBC2 = prmtop.OBC2
class GromacsTopFile(object):
"""GromacsTopFile parses a Gromacs top file and constructs a Topology and (optionally) an OpenMM System from it."""
class _MoleculeType(object):
"""Inner class to store information about a molecule type."""
def __init__(self):
......@@ -60,7 +60,7 @@ class GromacsTopFile(object):
self.exclusions = []
self.pairs = []
self.cmaps = []
def _processFile(self, file):
append = ''
for line in open(file):
......@@ -69,7 +69,7 @@ class GromacsTopFile(object):
else:
self._processLine(append+' '+line, file)
append = ''
def _processLine(self, line, file):
"""Process one line from a file."""
if ';' in line:
......@@ -79,13 +79,13 @@ class GromacsTopFile(object):
if stripped.startswith('*') or len(stripped) == 0:
# A comment or empty line.
return
elif stripped.startswith('[') and not ignore:
# The start of a category.
if not stripped.endswith(']'):
raise ValueError('Illegal line in .top file: '+line)
self._currentCategory = stripped[1:-1].strip()
elif stripped.startswith('#'):
# A preprocessor command.
fields = stripped.split()
......@@ -127,7 +127,7 @@ class GromacsTopFile(object):
if len(self._ifStack) == 0:
raise ValueError('Unexpected line in .top file: '+line)
del(self._ifStack[-1])
elif not ignore:
# A line of data for the current category
if self._currentCategory is None:
......@@ -166,7 +166,7 @@ class GromacsTopFile(object):
self._processPairType(line)
elif self._currentCategory == 'cmaptypes':
self._processCmapType(line)
def _processDefaults(self, line):
"""Process the [ defaults ] line."""
fields = line.split()
......@@ -179,7 +179,7 @@ class GromacsTopFile(object):
if fields[2].lower() == 'no':
raise ValueError('gen_pairs=no is not supported')
self._defaults = fields
def _processMoleculeType(self, line):
"""Process a line in the [ moleculetypes ] category."""
fields = line.split()
......@@ -188,14 +188,14 @@ class GromacsTopFile(object):
type = GromacsTopFile._MoleculeType()
self._moleculeTypes[fields[0]] = type
self._currentMoleculeType = type
def _processMolecule(self, line):
"""Process a line in the [ molecules ] category."""
fields = line.split()
if len(fields) < 2:
raise ValueError('Too few fields in [ molecules ] line: '+line);
self._molecules.append((fields[0], int(fields[1])))
def _processAtom(self, line):
"""Process a line in the [ atoms ] category."""
if self._currentMoleculeType is None:
......@@ -204,7 +204,7 @@ class GromacsTopFile(object):
if len(fields) < 5:
raise ValueError('Too few fields in [ atoms ] line: '+line);
self._currentMoleculeType.atoms.append(fields)
def _processBond(self, line):
"""Process a line in the [ bonds ] category."""
if self._currentMoleculeType is None:
......@@ -215,7 +215,7 @@ class GromacsTopFile(object):
if fields[2] != '1':
raise ValueError('Unsupported function type in [ bonds ] line: '+line);
self._currentMoleculeType.bonds.append(fields)
def _processAngle(self, line):
"""Process a line in the [ angles ] category."""
if self._currentMoleculeType is None:
......@@ -226,7 +226,7 @@ class GromacsTopFile(object):
if fields[3] not in ('1', '5'):
raise ValueError('Unsupported function type in [ angles ] line: '+line);
self._currentMoleculeType.angles.append(fields)
def _processDihedral(self, line):
"""Process a line in the [ dihedrals ] category."""
if self._currentMoleculeType is None:
......@@ -237,7 +237,7 @@ class GromacsTopFile(object):
if fields[4] not in ('1', '2', '3', '4', '9'):
raise ValueError('Unsupported function type in [ dihedrals ] line: '+line);
self._currentMoleculeType.dihedrals.append(fields)
def _processExclusion(self, line):
"""Process a line in the [ exclusions ] category."""
if self._currentMoleculeType is None:
......@@ -246,7 +246,7 @@ class GromacsTopFile(object):
if len(fields) < 2:
raise ValueError('Too few fields in [ exclusions ] line: '+line);
self._currentMoleculeType.exclusions.append(fields)
def _processPair(self, line):
"""Process a line in the [ pairs ] category."""
if self._currentMoleculeType is None:
......@@ -257,7 +257,7 @@ class GromacsTopFile(object):
if fields[2] != '1':
raise ValueError('Unsupported function type in [ pairs ] line: '+line);
self._currentMoleculeType.pairs.append(fields)
def _processCmap(self, line):
"""Process a line in the [ cmaps ] category."""
if self._currentMoleculeType is None:
......@@ -266,14 +266,14 @@ class GromacsTopFile(object):
if len(fields) < 6:
raise ValueError('Too few fields in [ pairs ] line: '+line);
self._currentMoleculeType.cmaps.append(fields)
def _processAtomType(self, line):
"""Process a line in the [ atomtypes ] category."""
fields = line.split()
if len(fields) < 7:
raise ValueError('Too few fields in [ atomtypes ] line: '+line);
self._atomTypes[fields[0]] = fields
def _processBondType(self, line):
"""Process a line in the [ bondtypes ] category."""
fields = line.split()
......@@ -282,7 +282,7 @@ class GromacsTopFile(object):
if fields[2] != '1':
raise ValueError('Unsupported function type in [ bondtypes ] line: '+line);
self._bondTypes[tuple(fields[:2])] = fields
def _processAngleType(self, line):
"""Process a line in the [ angletypes ] category."""
fields = line.split()
......@@ -291,7 +291,7 @@ class GromacsTopFile(object):
if fields[3] not in ('1', '5'):
raise ValueError('Unsupported function type in [ angletypes ] line: '+line);
self._angleTypes[tuple(fields[:3])] = fields
def _processDihedralType(self, line):
"""Process a line in the [ dihedraltypes ] category."""
fields = line.split()
......@@ -305,14 +305,14 @@ class GromacsTopFile(object):
self._dihedralTypes[key].append(fields)
else:
self._dihedralTypes[key] = [fields]
def _processImplicitType(self, line):
"""Process a line in the [ implicit_genborn_params ] category."""
fields = line.split()
if len(fields) < 6:
raise ValueError('Too few fields in [ implicit_genborn_params ] line: '+line);
self._implicitTypes[fields[0]] = fields
def _processPairType(self, line):
"""Process a line in the [ pairtypes ] category."""
fields = line.split()
......@@ -321,7 +321,7 @@ class GromacsTopFile(object):
if fields[2] != '1':
raise ValueError('Unsupported function type in [ pairtypes ] line: '+line);
self._pairTypes[tuple(fields[:2])] = fields
def _processCmapType(self, line):
"""Process a line in the [ cmaptypes ] category."""
fields = line.split()
......@@ -330,10 +330,10 @@ class GromacsTopFile(object):
if fields[5] != '1':
raise ValueError('Unsupported function type in [ cmaptypes ] line: '+line);
self._cmapTypes[tuple(fields[:5])] = fields
def __init__(self, file, unitCellDimensions=None, includeDir='/usr/local/gromacs/share/gromacs/top', defines={}):
"""Load a top file.
Parameters:
- file (string) the name of the file to load
- unitCellDimensions (Vec3=None) the dimensions of the crystallographic unit cell
......@@ -343,9 +343,9 @@ class GromacsTopFile(object):
"""
self._includeDirs = (os.path.dirname(file), includeDir)
self._defines = defines
# Parse the file.
self._currentCategory = None
self._ifStack = []
self._moleculeTypes = {}
......@@ -359,9 +359,9 @@ class GromacsTopFile(object):
self._pairTypes = {}
self._cmapTypes = {}
self._processFile(file)
# Create the Topology from it.
top = Topology()
## The Topology read from the prmtop file
self.topology = top
......@@ -371,9 +371,9 @@ class GromacsTopFile(object):
if moleculeName not in self._moleculeTypes:
raise ValueError("Unknown molecule type: "+moleculeName)
moleculeType = self._moleculeTypes[moleculeName]
# Create the specified number of molecules of this type.
for i in range(moleculeCount):
atoms = []
lastResidue = None
......@@ -393,9 +393,9 @@ class GromacsTopFile(object):
atomName = fields[4]
if atomName in atomReplacements:
atomName = atomReplacements[atomName]
# Try to guess the element.
upper = atomName.upper()
if upper.startswith('CL'):
element = elem.chlorine
......@@ -409,16 +409,16 @@ class GromacsTopFile(object):
except KeyError:
element = None
atoms.append(top.addAtom(atomName, element, r))
# Add bonds to the topology
for fields in moleculeType.bonds:
top.addBond(atoms[int(fields[0])-1], atoms[int(fields[1])-1])
def createSystem(self, nonbondedMethod=ff.NoCutoff, nonbondedCutoff=1.0*unit.nanometer,
constraints=None, rigidWater=True, implicitSolvent=None, soluteDielectric=1.0, solventDielectric=78.5, ewaldErrorTolerance=0.0005, removeCMMotion=True):
"""Construct an OpenMM System representing the topology described by this prmtop file.
Parameters:
- nonbondedMethod (object=NoCutoff) The method to use for nonbonded interactions. Allowed values are
NoCutoff, CutoffNonPeriodic, CutoffPeriodic, Ewald, or PME.
......@@ -434,7 +434,7 @@ class GromacsTopFile(object):
Returns: the newly created System
"""
# Create the System.
sys = mm.System()
boxSize = self.topology.getUnitCellDimensions()
if boxSize is not None:
......@@ -462,33 +462,33 @@ class GromacsTopFile(object):
topologyAtoms = list(self.topology.atoms())
exceptions = []
fudgeQQ = float(self._defaults[4])
# Loop over molecules and create the specified number of each type.
for moleculeName, moleculeCount in self._molecules:
moleculeType = self._moleculeTypes[moleculeName]
for i in range(moleculeCount):
# Record the types of all atoms.
baseAtomIndex = sys.getNumParticles()
atomTypes = [atom[1] for atom in moleculeType.atoms]
try:
[self._atomTypes[t][1] for t in atomTypes]
except KeyError as e:
raise ValueError('Unknown atom type: '+e.message)
# Add atoms.
for fields in moleculeType.atoms:
if len(fields) >= 8:
mass = float(fields[7])
else:
mass = float(self._atomTypes[fields[1]][2])
sys.addParticle(mass)
# Add bonds.
atomBonds = [{} for x in range(len(moleculeType.atoms))]
for fields in moleculeType.bonds:
atoms = [int(x)-1 for x in fields[:2]]
......@@ -523,9 +523,9 @@ class GromacsTopFile(object):
# Record information that will be needed for constraining angles.
atomBonds[atoms[0]][atoms[1]] = length
atomBonds[atoms[1]][atoms[0]] = length
# Add angles.
degToRad = math.pi/180
for fields in moleculeType.angles:
atoms = [int(x)-1 for x in fields[:3]]
......@@ -572,7 +572,7 @@ class GromacsTopFile(object):
bonds.addBond(baseAtomIndex+atoms[0], baseAtomIndex+atoms[2], float(params[2]), k)
# Add torsions.
for fields in moleculeType.dihedrals:
atoms = [int(x)-1 for x in fields[:4]]
types = tuple(atomTypes[i] for i in atoms)
......@@ -618,7 +618,7 @@ class GromacsTopFile(object):
rb = mm.RBTorsionForce()
sys.addForce(rb)
rb.addTorsion(baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], baseAtomIndex+atoms[2], baseAtomIndex+atoms[3], c[0], c[1], c[2], c[3], c[4], c[5])
# Add CMAP terms.
for fields in moleculeType.cmaps:
......@@ -649,7 +649,7 @@ class GromacsTopFile(object):
baseAtomIndex+atoms[1], baseAtomIndex+atoms[2], baseAtomIndex+atoms[3], baseAtomIndex+atoms[4])
# Set nonbonded parameters for particles.
for fields in moleculeType.atoms:
params = self._atomTypes[fields[1]]
if len(fields) > 6:
......@@ -665,9 +665,9 @@ class GromacsTopFile(object):
for fields in moleculeType.bonds:
atoms = [int(x)-1 for x in fields[:2]]
bondIndices.append((baseAtomIndex+atoms[0], baseAtomIndex+atoms[1]))
# Record nonbonded exceptions.
for fields in moleculeType.pairs:
atoms = [int(x)-1 for x in fields[:2]]
types = tuple(atomTypes[i] for i in atoms)
......@@ -682,15 +682,15 @@ class GromacsTopFile(object):
atom1params = nb.getParticleParameters(baseAtomIndex+atoms[0])
atom2params = nb.getParticleParameters(baseAtomIndex+atoms[1])
exceptions.append((baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], atom1params[0]*atom2params[0]*fudgeQQ, params[0], params[1]))
# Create nonbonded exceptions.
nb.createExceptionsFromBonds(bondIndices, fudgeQQ, float(self._defaults[3]))
for exception in exceptions:
nb.addException(exception[0], exception[1], exception[2], float(exception[3]), float(exception[4]), True)
# Finish configuring the NonbondedForce.
methodMap = {ff.NoCutoff:mm.NonbondedForce.NoCutoff,
ff.CutoffNonPeriodic:mm.NonbondedForce.CutoffNonPeriodic,
ff.CutoffPeriodic:mm.NonbondedForce.CutoffPeriodic,
......
......@@ -16,7 +16,7 @@ Portions copyright (c) 2012-2013 Stanford University and the Authors.
Authors: Randall J. Radmer, John D. Chodera, Peter Eastman
Contributors: Christoph Klein, Michael R. Shirts
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -92,7 +92,7 @@ class PrmtopLoader(object):
>>> import os, os.path
>>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-explicit')
>>> prmtop_filename = os.path.join(directory, 'alanine-dipeptide.prmtop')
>>> prmtop = PrmtopLoader(prmtop_filename)
>>> prmtop = PrmtopLoader(prmtop_filename)
"""
def __init__(self, inFilename):
......@@ -101,7 +101,7 @@ class PrmtopLoader(object):
ARGUMENTS
inFilename (string) - AMBER 'new-style' prmtop file, probably generated with one of the AMBER tleap/xleap/sleap
inFilename (string) - AMBER 'new-style' prmtop file, probably generated with one of the AMBER tleap/xleap/sleap
"""
......@@ -152,7 +152,7 @@ class PrmtopLoader(object):
Parameter:
- pointerLabel: a string matching one of the following:
NATOM : total number of atoms
NATOM : total number of atoms
NTYPES : total number of distinct atom types
NBONH : number of bonds containing hydrogen
MBONA : number of bonds not containing hydrogen
......@@ -183,7 +183,7 @@ class PrmtopLoader(object):
NMXRS : number of atoms in the largest residue
IFCAP : set to 1 if the CAP option from edit was specified
"""
index = POINTER_LABEL_LIST.index(pointerLabel)
index = POINTER_LABEL_LIST.index(pointerLabel)
return float(self._raw_data['POINTERS'][index])
def getNumAtoms(self):
......@@ -350,7 +350,7 @@ class PrmtopLoader(object):
bondPointers=self._raw_data["BONDS_INC_HYDROGEN"]
self._bondListWithH = self._getBonds(bondPointers)
return self._bondListWithH
def getBondsNoH(self):
"""Return list of bonded atom pairs, K, and Rmin for each bond with no hydrogen"""
......@@ -509,11 +509,11 @@ class PrmtopLoader(object):
def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmodel=None, soluteDielectric=1.0, solventDielectric=78.5, nonbondedCutoff=None, nonbondedMethod='NoCutoff', scee=1.2, scnb=2.0, mm=None, verbose=False, EwaldErrorTolerance=None, flexibleConstraints=True, rigidWater=True):
"""
Create an OpenMM System from an Amber prmtop file.
ARGUMENTS (specify one or the other, but not both)
prmtop_filename (String) - name of Amber prmtop file (new-style only)
prmtop_loader (PrmtopLoader) - the loaded prmtop file
OPTIONAL ARGUMENTS
shake (String) - if 'h-bonds', will SHAKE all bonds to hydrogen and water; if 'all-bonds', will SHAKE all bonds and water (default: None)
gbmodel (String) - if 'OBC', OBC GBSA will be used; if 'GBVI', GB/VI will be used (default: None)
......@@ -547,10 +547,10 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
>>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-explicit')
>>> prmtop_filename = os.path.join(directory, 'alanine-dipeptide.prmtop')
>>> system = readAmberSystem(prmtop_filename)
>>> system = readAmberSystem(prmtop_filename)
"""
if prmtop_filename is None and prmtop_loader is None:
raise Exception("Must specify a filename or loader")
if prmtop_filename is not None and prmtop_loader is not None:
......@@ -567,7 +567,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
if prmtop.getIfPert()>0:
raise Exception("perturbation not currently supported")
if prmtop.getIfBox()>1:
raise Exception("only standard periodic boxes are currently supported")
......@@ -596,9 +596,9 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH():
if isWater[iAtom] and isWater[jAtom]:
system.addConstraint(iAtom, jAtom, rMin)
# Add harmonic bonds.
if verbose: print "Adding bonds..."
if verbose: print "Adding bonds..."
force = mm.HarmonicBondForce()
if flexibleConstraints or (shake not in ('h-bonds', 'all-bonds', 'h-angles')):
for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH():
......@@ -610,7 +610,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
system.addForce(force)
# Add harmonic angles.
if verbose: print "Adding angles..."
if verbose: print "Adding angles..."
force = mm.HarmonicAngleForce()
if shake == 'h-angles':
numConstrainedBonds = system.getNumConstraints()
......@@ -638,7 +638,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
l1 = bond[1]
elif bond[0] == kAtom:
l2 = bond[1]
# Compute the distance between atoms and add a constraint
length = math.sqrt(l1*l1 + l2*l2 - 2*l1*l2*math.cos(aMin))
system.addConstraint(iAtom, kAtom, length)
......@@ -647,14 +647,14 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
system.addForce(force)
# Add torsions.
if verbose: print "Adding torsions..."
if verbose: print "Adding torsions..."
force = mm.PeriodicTorsionForce()
for (iAtom, jAtom, kAtom, lAtom, forceConstant, phase, periodicity) in prmtop.getDihedrals():
force.addTorsion(iAtom, jAtom, kAtom, lAtom, periodicity, phase, forceConstant)
system.addForce(force)
# Add nonbonded interactions.
if verbose: print "Adding nonbonded interactions..."
if verbose: print "Adding nonbonded interactions..."
force = mm.NonbondedForce()
if (prmtop.getIfBox() == 0):
# System is non-periodic.
......@@ -663,12 +663,12 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
elif nonbondedMethod == 'CutoffNonPeriodic':
if nonbondedCutoff is None:
raise Exception("No cutoff value specified")
force.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
force.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
force.setCutoffDistance(nonbondedCutoff)
else:
raise Exception("Illegal nonbonded method for a non-periodic system")
else:
# System is periodic.
# System is periodic.
# Set periodic box vectors for periodic system
(boxBeta, boxX, boxY, boxZ) = prmtop.getBoxBetaAndDimensions()
d0 = units.Quantity(0.0, units.angstroms)
......@@ -676,16 +676,16 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
yVec = units.Quantity((d0, boxY, d0))
zVec = units.Quantity((d0, d0, boxZ))
system.setDefaultPeriodicBoxVectors(xVec, yVec, zVec)
# Set cutoff.
if nonbondedCutoff is None:
# Compute cutoff automatically.
min_box_width = min([boxX / units.nanometers, boxY / units.nanometers, boxZ / units.nanometers])
CLEARANCE_FACTOR = 0.97 # reduce the cutoff to be a bit smaller than 1/2 smallest box length
CLEARANCE_FACTOR = 0.97 # reduce the cutoff to be a bit smaller than 1/2 smallest box length
nonbondedCutoff = units.Quantity((min_box_width * CLEARANCE_FACTOR) / 2.0, units.nanometers)
if nonbondedMethod != 'NoCutoff':
force.setCutoffDistance(nonbondedCutoff)
# Set nonbonded method.
if nonbondedMethod == 'NoCutoff':
force.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
......@@ -724,7 +724,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
excludeParams = (0.0, 0.1, 0.0)
for iAtom in range(prmtop.getNumAtoms()):
for jAtom in excludedAtoms[iAtom]:
if min((iAtom, jAtom), (jAtom, iAtom)) in excludedAtomPairs: continue
if min((iAtom, jAtom), (jAtom, iAtom)) in excludedAtomPairs: continue
force.addException(iAtom, jAtom, excludeParams[0], excludeParams[1], excludeParams[2])
system.addForce(force)
......@@ -812,7 +812,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
if gbmodel == 'OBC2':
gb.addParticle(charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1])
else:
gb.addParticle([charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1]])
gb.addParticle([charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1]])
system.addForce(gb)
if nonbondedMethod == 'NoCutoff':
gb.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
......@@ -836,7 +836,7 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
def readAmberCoordinates(filename, read_box=False, read_velocities=False, verbose=False, asNumpy=False):
"""
Read atomic coordinates (and optionally, box vectors) from Amber formatted coordinate file.
Read atomic coordinates (and optionally, box vectors) from Amber formatted coordinate file.
ARGUMENTS
......@@ -853,13 +853,13 @@ def readAmberCoordinates(filename, read_box=False, read_velocities=False, verbos
Read coordinates in vacuum.
>>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-gbsa')
>>> crd_filename = os.path.join(directory, 'alanine-dipeptide.inpcrd')
>>> crd_filename = os.path.join(directory, 'alanine-dipeptide.inpcrd')
>>> coordinates = readAmberCoordinates(crd_filename)
Read coordinates in solvent.
>>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-explicit')
>>> crd_filename = os.path.join(directory, 'alanine-dipeptide.inpcrd')
>>> crd_filename = os.path.join(directory, 'alanine-dipeptide.inpcrd')
>>> [coordinates, box_vectors] = readAmberCoordinates(crd_filename, read_box=True)
"""
......@@ -922,7 +922,7 @@ def readAmberCoordinates(filename, read_box=False, read_velocities=False, verbos
velocities = newvel
# Assign units.
velocities = units.Quantity(velocities, units.angstroms/units.picoseconds)
# Read box size if present
box_vectors = None
if (read_box):
......@@ -944,20 +944,20 @@ def readAmberCoordinates(filename, read_box=False, read_velocities=False, verbos
else:
a = units.Quantity(mm.Vec3(box_dimensions[0], 0.0, 0.0), units.angstroms)
b = units.Quantity(mm.Vec3(0.0, box_dimensions[1], 0.0), units.angstroms)
c = units.Quantity(mm.Vec3(0.0, 0.0, box_dimensions[2]), units.angstroms)
c = units.Quantity(mm.Vec3(0.0, 0.0, box_dimensions[2]), units.angstroms)
box_vectors = [a,b,c]
else:
raise Exception("Don't know what to do with box vectors: %s" % line)
# Close file
infile.close()
if box_vectors and velocities:
return (coordinates, box_vectors, velocities)
if box_vectors:
return (coordinates, box_vectors)
if velocities:
return (coordinates, velocities)
return (coordinates, velocities)
return coordinates
#=============================================================================================
......
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012 University of Virginia and the Authors.
Authors: Christoph Klein, Michael R. Shirts
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -35,15 +35,15 @@ import sys, pdb, pickle
d0=[2.26685,2.32548,2.38397,2.44235,2.50057,2.55867,2.61663,2.67444,
2.73212,2.78965,2.84705,2.9043,2.96141,3.0184,3.07524,3.13196,
3.18854,3.24498,3.30132,3.35752,3.4136,
2.31191,2.37017,2.4283,2.48632,2.5442,2.60197,2.65961,2.71711,
2.77449,2.83175,2.88887,2.94586,3.00273,3.05948,3.1161,3.1726,
3.22897,3.28522,3.34136,3.39738,3.45072,
2.35759,2.41549,2.47329,2.53097,2.58854,2.646,2.70333,2.76056,
2.31191,2.37017,2.4283,2.48632,2.5442,2.60197,2.65961,2.71711,
2.77449,2.83175,2.88887,2.94586,3.00273,3.05948,3.1161,3.1726,
3.22897,3.28522,3.34136,3.39738,3.45072,
2.35759,2.41549,2.47329,2.53097,2.58854,2.646,2.70333,2.76056,
2.81766,2.87465,2.93152,2.98827,3.0449,3.10142,3.15782,3.21411,
3.27028,3.32634,3.3823,3.43813,3.49387,
2.4038,2.46138,2.51885,2.57623,2.63351,2.69067,2.74773,2.80469,
2.86152,2.91826,2.97489,3.0314,3.08781,3.1441,3.20031,3.25638,
3.31237,3.36825,3.42402,3.4797,3.53527,
3.31237,3.36825,3.42402,3.4797,3.53527,
2.45045,2.50773,2.56492,2.62201,2.679,2.7359,2.7927,2.8494,2.90599,
2.9625,3.0189,3.07518,3.13138,3.18748,3.24347,3.29937,3.35515,
3.41085,3.46646,3.52196,3.57738,
......@@ -53,134 +53,134 @@ d0=[2.26685,2.32548,2.38397,2.44235,2.50057,2.55867,2.61663,2.67444,
2.54489,2.60164,2.6583,2.71488,2.77134,2.8278,2.88412,2.94034,
2.9965,3.05256,3.10853,3.16442,3.22021,3.27592,3.33154,3.38707,
3.44253,3.49789,3.55316,3.60836,3.66348,
2.59259,2.6491,2.70553,2.76188,2.81815,2.87434,2.93044,2.98646,
3.04241,3.09827,3.15404,3.20974,3.26536,3.32089,3.37633,3.4317,
3.48699,3.54219,3.59731,3.65237,3.70734,
2.59259,2.6491,2.70553,2.76188,2.81815,2.87434,2.93044,2.98646,
3.04241,3.09827,3.15404,3.20974,3.26536,3.32089,3.37633,3.4317,
3.48699,3.54219,3.59731,3.65237,3.70734,
2.64054,2.69684,2.75305,2.80918,2.86523,2.92122,2.97712,3.03295,
3.0887,3.14437,3.19996,3.25548,3.31091,3.36627,3.42156,3.47677,
3.5319,3.58695,3.64193,3.69684,3.75167,
2.68873,2.74482,2.80083,2.85676,2.91262,2.96841,3.02412,3.07976,
3.13533,3.19082,3.24623,3.30157,3.35685,3.41205,3.46718,3.52223,
3.57721,3.63213,3.68696,3.74174,3.79644,
2.73713,2.79302,2.84884,2.90459,2.96027,3.01587,3.0714,3.12686,
3.0887,3.14437,3.19996,3.25548,3.31091,3.36627,3.42156,3.47677,
3.5319,3.58695,3.64193,3.69684,3.75167,
2.68873,2.74482,2.80083,2.85676,2.91262,2.96841,3.02412,3.07976,
3.13533,3.19082,3.24623,3.30157,3.35685,3.41205,3.46718,3.52223,
3.57721,3.63213,3.68696,3.74174,3.79644,
2.73713,2.79302,2.84884,2.90459,2.96027,3.01587,3.0714,3.12686,
3.18225,3.23757,3.29282,3.34801,3.40313,3.45815,3.51315,3.56805,
3.6229,3.67767,3.73237,3.78701,3.84159,
3.6229,3.67767,3.73237,3.78701,3.84159,
2.78572,2.84143,2.89707,2.95264,3.00813,3.06356,3.11892,3.17422,
3.22946,3.28462,3.33971,3.39474,3.44971,3.5046,3.55944,3.61421,
3.66891,3.72356,3.77814,3.83264,3.8871,
2.83446,2.89,2.94547,3.00088,3.05621,3.11147,3.16669,3.22183,
3.27689,3.33191,3.38685,3.44174,3.49656,3.55132,3.60602,3.66066,
3.71523,3.76975,3.82421,3.8786,3.93293,
2.88335,2.93873,2.99404,3.04929,3.10447,3.15959,3.21464,3.26963,
3.32456,3.37943,3.43424,3.48898,3.54366,3.5983,3.65287,3.70737,
3.76183,3.81622,3.87056,3.92484,3.97905,
2.93234,2.9876,3.04277,3.09786,3.15291,3.20787,3.26278,3.31764,
3.37242,3.42716,3.48184,3.53662,3.591,3.64551,3.69995,3.75435,
3.80867,3.86295,3.91718,3.97134,4.02545,
2.98151,3.0366,3.09163,3.14659,3.20149,3.25632,3.3111,3.36581,
3.42047,3.47507,3.52963,3.58411,3.63855,3.69293,3.74725,3.80153,
3.85575,3.90991,3.96403,4.01809,4.07211,
3.03074,3.08571,3.14061,3.19543,3.25021,3.30491,3.35956,3.41415,
3.46869,3.52317,3.57759,3.63196,3.68628,3.74054,3.79476,3.84893,
3.90303,3.95709,4.01111,4.06506,4.11897,
3.08008,3.13492,3.1897,3.2444,3.29905,3.35363,3.40815,3.46263,
3.22946,3.28462,3.33971,3.39474,3.44971,3.5046,3.55944,3.61421,
3.66891,3.72356,3.77814,3.83264,3.8871,
2.83446,2.89,2.94547,3.00088,3.05621,3.11147,3.16669,3.22183,
3.27689,3.33191,3.38685,3.44174,3.49656,3.55132,3.60602,3.66066,
3.71523,3.76975,3.82421,3.8786,3.93293,
2.88335,2.93873,2.99404,3.04929,3.10447,3.15959,3.21464,3.26963,
3.32456,3.37943,3.43424,3.48898,3.54366,3.5983,3.65287,3.70737,
3.76183,3.81622,3.87056,3.92484,3.97905,
2.93234,2.9876,3.04277,3.09786,3.15291,3.20787,3.26278,3.31764,
3.37242,3.42716,3.48184,3.53662,3.591,3.64551,3.69995,3.75435,
3.80867,3.86295,3.91718,3.97134,4.02545,
2.98151,3.0366,3.09163,3.14659,3.20149,3.25632,3.3111,3.36581,
3.42047,3.47507,3.52963,3.58411,3.63855,3.69293,3.74725,3.80153,
3.85575,3.90991,3.96403,4.01809,4.07211,
3.03074,3.08571,3.14061,3.19543,3.25021,3.30491,3.35956,3.41415,
3.46869,3.52317,3.57759,3.63196,3.68628,3.74054,3.79476,3.84893,
3.90303,3.95709,4.01111,4.06506,4.11897,
3.08008,3.13492,3.1897,3.2444,3.29905,3.35363,3.40815,3.46263,
3.51704,3.57141,3.62572,3.67998,3.73418,3.78834,3.84244,3.8965,
3.95051,4.00447,4.05837,4.11224,4.16605,
3.95051,4.00447,4.05837,4.11224,4.16605,
3.12949,3.18422,3.23888,3.29347,3.348,3.40247,3.45688,3.51124,
3.56554,3.6198,3.674,3.72815,3.78225,3.83629,3.8903,3.94425,
3.99816,4.05203,4.10583,4.15961,4.21333,
3.56554,3.6198,3.674,3.72815,3.78225,3.83629,3.8903,3.94425,
3.99816,4.05203,4.10583,4.15961,4.21333,
3.17899,3.23361,3.28815,3.34264,3.39706,3.45142,3.50571,3.55997,
3.61416,3.66831,3.72241,3.77645,3.83046,3.8844,3.93831,3.99216,
4.04598,4.09974,4.15347,4.20715,4.26078,
3.22855,3.28307,3.33751,3.39188,3.4462,3.50046,3.55466,3.6088,
3.6629,3.71694,3.77095,3.82489,3.8788,3.93265,3.98646,4.04022,
3.61416,3.66831,3.72241,3.77645,3.83046,3.8844,3.93831,3.99216,
4.04598,4.09974,4.15347,4.20715,4.26078,
3.22855,3.28307,3.33751,3.39188,3.4462,3.50046,3.55466,3.6088,
3.6629,3.71694,3.77095,3.82489,3.8788,3.93265,3.98646,4.04022,
4.09395,4.14762,4.20126,4.25485,4.3084]
m0=[0.0381511,0.0338587,0.0301776,0.027003,0.0242506,0.0218529,
0.0197547,0.0179109,0.0162844,0.0148442,0.0135647,0.0124243,
0.0114047,0.0104906,0.00966876,0.008928,0.0082587,0.00765255,
0.00710237,0.00660196,0.00614589,
0.0396198,0.0351837,0.0313767,0.0280911,0.0252409,0.0227563,
0.0205808,0.0186681,0.0169799,0.0154843,0.014155,0.0129696,
0.0119094,0.0109584,0.0101031,0.00933189,0.0086348,0.00800326,
0.00742986,0.00690814,0.00643255,
0.041048,0.0364738,0.0325456,0.0291532,0.0262084,0.0236399,
0.0213897,0.0194102,0.0176622,0.0161129,0.0147351,0.0135059,
0.0124061,0.0114192,0.0105312,0.00973027,0.00900602,0.00834965,
0.0077535,0.00721091,0.00671609,
0.0424365,0.0377295,0.0336846,0.0301893,0.0271533,0.0245038,
0.0221813,0.0201371,0.018331,0.0167295,0.0153047,0.014033,
0.0128946,0.0118727,0.0109529,0.0101229,0.00937212,0.00869147,
0.00807306,0.00751003,0.00699641,
m0=[0.0381511,0.0338587,0.0301776,0.027003,0.0242506,0.0218529,
0.0197547,0.0179109,0.0162844,0.0148442,0.0135647,0.0124243,
0.0114047,0.0104906,0.00966876,0.008928,0.0082587,0.00765255,
0.00710237,0.00660196,0.00614589,
0.0396198,0.0351837,0.0313767,0.0280911,0.0252409,0.0227563,
0.0205808,0.0186681,0.0169799,0.0154843,0.014155,0.0129696,
0.0119094,0.0109584,0.0101031,0.00933189,0.0086348,0.00800326,
0.00742986,0.00690814,0.00643255,
0.041048,0.0364738,0.0325456,0.0291532,0.0262084,0.0236399,
0.0213897,0.0194102,0.0176622,0.0161129,0.0147351,0.0135059,
0.0124061,0.0114192,0.0105312,0.00973027,0.00900602,0.00834965,
0.0077535,0.00721091,0.00671609,
0.0424365,0.0377295,0.0336846,0.0301893,0.0271533,0.0245038,
0.0221813,0.0201371,0.018331,0.0167295,0.0153047,0.014033,
0.0128946,0.0118727,0.0109529,0.0101229,0.00937212,0.00869147,
0.00807306,0.00751003,0.00699641,
0.0437861,0.0389516,0.0347944,0.0311998,0.0280758,0.0253479,
0.0229555,0.0208487,0.0189864,0.0173343,0.0158637,0.0145507,
0.0133748,0.0123188,0.0113679,0.0105096,0.0097329,0.00902853,
0.00838835,0.00780533,0.0072733,
0.0450979,0.0401406,0.0358753,0.0321851,0.0289761,0.0261726,
0.0237125,0.0215451,0.0196282,0.017927,0.0164121,0.0150588,
0.0138465,0.0127573,0.0117761,0.0108902,0.0100882,0.00936068,
0.00869923,0.00809665,0.00754661,
0.0463729,0.0412976,0.0369281,0.0331456,0.0298547,0.026978,
0.0244525,0.0222264,0.0202567,0.0185078,0.0169498,0.0155575,
0.0143096,0.0131881,0.0121775,0.0112646,0.010438,0.00968781,
0.0229555,0.0208487,0.0189864,0.0173343,0.0158637,0.0145507,
0.0133748,0.0123188,0.0113679,0.0105096,0.0097329,0.00902853,
0.00838835,0.00780533,0.0072733,
0.0450979,0.0401406,0.0358753,0.0321851,0.0289761,0.0261726,
0.0237125,0.0215451,0.0196282,0.017927,0.0164121,0.0150588,
0.0138465,0.0127573,0.0117761,0.0108902,0.0100882,0.00936068,
0.00869923,0.00809665,0.00754661,
0.0463729,0.0412976,0.0369281,0.0331456,0.0298547,0.026978,
0.0244525,0.0222264,0.0202567,0.0185078,0.0169498,0.0155575,
0.0143096,0.0131881,0.0121775,0.0112646,0.010438,0.00968781,
0.00900559,0.00838388,0.00781622,
0.0476123,0.0424233,0.0379534,0.034082,0.0307118,0.0277645,
0.0476123,0.0424233,0.0379534,0.034082,0.0307118,0.0277645,
0.0251757,0.0228927,0.0208718,0.0190767,0.0174768,0.0160466,
0.0147642,0.0136112,0.0125719,0.0116328,0.0107821,0.0100099,
0.00930735,0.00866695,0.00808206,
0.0488171,0.0435186,0.038952,0.0349947,0.0315481,0.0285324,
0.0258824,0.0235443,0.0214738,0.0196339,0.0179934,0.0165262,
0.0152103,0.0140267,0.0129595,0.0119947,0.0111206,0.0103268,
0.00960445,0.00894579,0.00834405,
0.0499883,0.0445845,0.0399246,0.0358844,0.032364,0.0292822,
0.0265729,0.0241815,0.0220629,0.0201794,0.0184994,0.0169964,
0.0156479,0.0144345,0.0133401,0.0123504,0.0114534,0.0106386,
0.00989687,0.00922037,0.00860216,
0.0511272,0.0456219,0.040872,0.0367518,0.0331599,0.0300142,
0.0147642,0.0136112,0.0125719,0.0116328,0.0107821,0.0100099,
0.00930735,0.00866695,0.00808206,
0.0488171,0.0435186,0.038952,0.0349947,0.0315481,0.0285324,
0.0258824,0.0235443,0.0214738,0.0196339,0.0179934,0.0165262,
0.0152103,0.0140267,0.0129595,0.0119947,0.0111206,0.0103268,
0.00960445,0.00894579,0.00834405,
0.0499883,0.0445845,0.0399246,0.0358844,0.032364,0.0292822,
0.0265729,0.0241815,0.0220629,0.0201794,0.0184994,0.0169964,
0.0156479,0.0144345,0.0133401,0.0123504,0.0114534,0.0106386,
0.00989687,0.00922037,0.00860216,
0.0511272,0.0456219,0.040872,0.0367518,0.0331599,0.0300142,
0.0272475,0.0248045,0.0226392,0.0207135,0.0189952,0.0174574,
0.0160771,0.0148348,0.0137138,0.0126998,0.0117805,0.0109452,
0.0101846,0.00949067,0.00885636,
0.0522348,0.0466315,0.0417948,0.0375973,0.0339365,0.030729,
0.0160771,0.0148348,0.0137138,0.0126998,0.0117805,0.0109452,
0.0101846,0.00949067,0.00885636,
0.0522348,0.0466315,0.0417948,0.0375973,0.0339365,0.030729,
0.0279067,0.0254136,0.023203,0.0212363,0.0194809,0.0179092,
0.016498,0.0152275,0.0140807,0.013043,0.012102,0.0112466,
0.0104676,0.00975668,0.00910664,
0.016498,0.0152275,0.0140807,0.013043,0.012102,0.0112466,
0.0104676,0.00975668,0.00910664,
0.0533123,0.0476145,0.042694,0.0384218,0.0346942,0.0314268,
0.0285507,0.026009,0.0237547,0.0217482,0.0199566,0.018352,
0.0169108,0.0156128,0.0144408,0.0133801,0.0124179,0.011543,
0.010746,0.0100184,0.00935302,
0.0285507,0.026009,0.0237547,0.0217482,0.0199566,0.018352,
0.0169108,0.0156128,0.0144408,0.0133801,0.0124179,0.011543,
0.010746,0.0100184,0.00935302,
0.0543606,0.0485716,0.04357,0.0392257,0.0354335,0.0321082,
0.02918,0.0265913,0.0242943,0.0222492,0.0204225,0.0187859,
0.0173155,0.0159908,0.0147943,0.0137111,0.0127282,0.0118343,
0.0110197,0.0102759,0.00959549,
0.0553807,0.0495037,0.0444239,0.0400097,0.0361551,0.0327736,
0.0297949,0.0271605,0.0248222,0.0227396,0.0208788,0.0192111,
0.0177122,0.0163615,0.0151413,0.0140361,0.013033,0.0121206,
0.0112888,0.0105292,0.00983409,
0.0563738,0.0504116,0.0452562,0.0407745,0.0368593,0.0334235,
0.0303958,0.0277171,0.0253387,0.0232197,0.0213257,0.0196277,
0.0181013,0.0167252,0.0154817,0.0143552,0.0133325,0.0124019,
0.0115534,0.0107783,0.0100688,
0.0573406,0.0512963,0.0460676,0.0415206,0.0375468,0.0340583,
0.030983,0.0282614,0.0258441,0.0236896,0.0217634,0.020036,
0.0184826,0.017082,0.0158158,0.0146685,0.0136266,0.0126783,
0.0118135,0.0110232,0.0102998,
0.0582822,0.0521584,0.0468589,0.0422486,0.038218,0.0346784,
0.02918,0.0265913,0.0242943,0.0222492,0.0204225,0.0187859,
0.0173155,0.0159908,0.0147943,0.0137111,0.0127282,0.0118343,
0.0110197,0.0102759,0.00959549,
0.0553807,0.0495037,0.0444239,0.0400097,0.0361551,0.0327736,
0.0297949,0.0271605,0.0248222,0.0227396,0.0208788,0.0192111,
0.0177122,0.0163615,0.0151413,0.0140361,0.013033,0.0121206,
0.0112888,0.0105292,0.00983409,
0.0563738,0.0504116,0.0452562,0.0407745,0.0368593,0.0334235,
0.0303958,0.0277171,0.0253387,0.0232197,0.0213257,0.0196277,
0.0181013,0.0167252,0.0154817,0.0143552,0.0133325,0.0124019,
0.0115534,0.0107783,0.0100688,
0.0573406,0.0512963,0.0460676,0.0415206,0.0375468,0.0340583,
0.030983,0.0282614,0.0258441,0.0236896,0.0217634,0.020036,
0.0184826,0.017082,0.0158158,0.0146685,0.0136266,0.0126783,
0.0118135,0.0110232,0.0102998,
0.0582822,0.0521584,0.0468589,0.0422486,0.038218,0.0346784,
0.0315571,0.0287938,0.0263386,0.0241497,0.0221922,0.0204362,
0.0188566,0.0174319,0.0161437,0.0149761,0.0139154,0.0129499,
0.0120691,0.0112641,0.0105269,
0.0591994,0.0529987,0.0476307,0.042959,0.0388734,0.0352843,
0.0188566,0.0174319,0.0161437,0.0149761,0.0139154,0.0129499,
0.0120691,0.0112641,0.0105269,
0.0591994,0.0529987,0.0476307,0.042959,0.0388734,0.0352843,
0.0321182,0.0293144,0.0268225,0.0246002,0.0226121,0.0208283,
0.0192232,0.0177751,0.0164654,0.015278,0.0141991,0.0132167,
0.0123204,0.0115009,0.0107504,
0.0600932,0.053818,0.0483836,0.0436525,0.0395136,0.0358764,
0.0192232,0.0177751,0.0164654,0.015278,0.0141991,0.0132167,
0.0123204,0.0115009,0.0107504,
0.0600932,0.053818,0.0483836,0.0436525,0.0395136,0.0358764,
0.0326669,0.0298237,0.0272961,0.0250413,0.0230236,0.0212126,
0.0195826,0.0181118,0.0167811,0.0155744,0.0144778,0.0134789,
0.0125673,0.0117338,0.0109702,
0.0609642,0.0546169,0.0491183,0.0443295,0.0401388,0.036455,
0.0332033,0.030322,0.0277596,0.0254732,0.0234266,0.0215892,
0.0199351,0.018442,0.0170909,0.0158654,0.0147514,0.0137365,
0.0195826,0.0181118,0.0167811,0.0155744,0.0144778,0.0134789,
0.0125673,0.0117338,0.0109702,
0.0609642,0.0546169,0.0491183,0.0443295,0.0401388,0.036455,
0.0332033,0.030322,0.0277596,0.0254732,0.0234266,0.0215892,
0.0199351,0.018442,0.0170909,0.0158654,0.0147514,0.0137365,
0.0128101,0.0119627,0.0111863]
# Rescale to nm
for i in range (len(d0)):
......@@ -196,7 +196,7 @@ Amber Equivalent: igb = 1
def GBSAHCTForce(solventDielectric=78.5, soluteDielectric=1, SA=None):
custom = CustomGBForce()
custom.addPerParticleParameter("q");
custom.addPerParticleParameter("radius");
custom.addPerParticleParameter("scale");
......@@ -213,7 +213,7 @@ def GBSAHCTForce(solventDielectric=78.5, soluteDielectric=1, SA=None):
custom.addComputedValue("B", "1/(1/or-I);"
"or=radius-offset", CustomGBForce.SingleParticle)
custom.addEnergyTerm("-0.5*138.935485*(1/soluteDielectric-1/solventDielectric)*q^2/B", CustomGBForce.SingleParticle)
if SA=='ACE':
custom.addEnergyTerm("28.3919551*(radius+0.14)^2*(radius/B)^6", CustomGBForce.SingleParticle)
......@@ -225,7 +225,7 @@ def GBSAHCTForce(solventDielectric=78.5, soluteDielectric=1, SA=None):
return custom
"""
Amber Equivalents: igb = 2
Amber Equivalents: igb = 2
"""
def GBSAOBC1Force(solventDielectric=78.5, soluteDielectric=1, SA=None):
......@@ -258,7 +258,7 @@ def GBSAOBC1Force(solventDielectric=78.5, soluteDielectric=1, SA=None):
return custom
"""
Amber Equivalents: igb = 5
Amber Equivalents: igb = 5
"""
def GBSAOBC2Force(solventDielectric=78.5, soluteDielectric=1, SA=None):
......@@ -295,27 +295,27 @@ Amber Equivalents: igb = 7
"""
def GBSAGBnForce(solventDielectric=78.5, soluteDielectric=1, SA=None):
"""
Indexing for tables:
input: radius1, radius2
index = (radius2*200-20)*21 + (radius1*200-20)
output: index of desired value in row-by-row, 1D version of Tables 3 & 4
"""
custom = CustomGBForce()
custom.addPerParticleParameter("q");
custom.addPerParticleParameter("radius");
custom.addPerParticleParameter("scale");
custom.addGlobalParameter("solventDielectric", solventDielectric);
custom.addGlobalParameter("soluteDielectric", soluteDielectric);
custom.addGlobalParameter("offset", 0.009)
custom.addGlobalParameter("neckScale", 0.361825)
custom.addGlobalParameter("neckCut", 0.68)
custom.addFunction("getd0", d0, 0, 440)
custom.addFunction("getm0", m0, 0, 440)
......@@ -328,10 +328,10 @@ def GBSAGBnForce(solventDielectric=78.5, soluteDielectric=1, SA=None):
"D=abs(r-sr2);"
"sr2 = scale2*or2;"
"or1 = radius1-offset; or2 = radius2-offset", CustomGBForce.ParticlePairNoExclusions)
custom.addComputedValue("B", "1/(1/or-tanh(1.09511284*psi-1.907992938*psi^2+2.50798245*psi^3)/radius);"
"psi=I*or; or=radius-offset", CustomGBForce.SingleParticle)
custom.addEnergyTerm("-0.5*138.935485*(1/soluteDielectric-1/solventDielectric)*q^2/B", CustomGBForce.SingleParticle)
if SA=='ACE':
custom.addEnergyTerm("28.3919551*(radius+0.14)^2*(radius/B)^6", CustomGBForce.SingleParticle)
......@@ -340,4 +340,4 @@ def GBSAGBnForce(solventDielectric=78.5, soluteDielectric=1, SA=None):
custom.addEnergyTerm("-138.935485*(1/soluteDielectric-1/solventDielectric)*q1*q2/f;"
"f=sqrt(r^2+B1*B2*exp(-r^2/(4*B1*B2)))", CustomGBForce.ParticlePairNoExclusions)
return custom
\ No newline at end of file
return custom
......@@ -12,7 +12,7 @@ Portions copyright (c) 2012 Stanford University and the Authors.
Authors: Christopher M. Bruns
Contributors: Peter Eastman
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -260,7 +260,7 @@ class PdbStructure(object):
"""Establish first and last residues, atoms, etc."""
for model in self.models:
model._finalize()
def get_unit_cell_dimensions(self):
"""Get the dimensions of the crystallographic unit cell (may be None)."""
return self._unit_cell_dimensions
......@@ -547,12 +547,12 @@ class Residue(object):
...
>>> for atom in res:
... print atom
ATOM 188 N CYS A 42 40.714 -5.292 12.123 1.00 11.29 N
ATOM 189 CA CYS A 42 39.736 -5.883 12.911 1.00 10.01 C
ATOM 190 C CYS A 42 40.339 -6.654 14.087 1.00 22.28 C
ATOM 191 O CYS A 42 41.181 -7.530 13.859 1.00 13.70 O
ATOM 192 CB CYS A 42 38.949 -6.825 12.002 1.00 9.67 C
ATOM 193 SG CYS A 42 37.557 -7.514 12.922 1.00 20.12 S
ATOM 188 N CYS A 42 40.714 -5.292 12.123 1.00 11.29 N
ATOM 189 CA CYS A 42 39.736 -5.883 12.911 1.00 10.01 C
ATOM 190 C CYS A 42 40.339 -6.654 14.087 1.00 22.28 C
ATOM 191 O CYS A 42 41.181 -7.530 13.859 1.00 13.70 O
ATOM 192 CB CYS A 42 38.949 -6.825 12.002 1.00 9.67 C
ATOM 193 SG CYS A 42 37.557 -7.514 12.922 1.00 20.12 S
"""
for atom in self.iter_atoms():
yield atom
......
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012-2013 Stanford University and the Authors.
Authors: Peter Eastman
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -48,19 +48,19 @@ from math import ceil, floor
class Modeller(object):
"""Modeller provides tools for editing molecular models, such as adding water or missing hydrogens.
To use it, create a Modeller object, specifying the initial Topology and atom positions. You can
then call various methods to change the model in different ways. Each time you do, a new Topology
and list of coordinates is created to represent the changed model. Finally, call getTopology()
and getPositions() to get the results.
"""
_residueHydrogens = {}
_hasLoadedStandardHydrogens = False
def __init__(self, topology, positions):
"""Create a new Modeller object
Parameters:
- topology (Topology) the initial Topology of the model
- positions (list) the initial atomic positions
......@@ -71,27 +71,27 @@ class Modeller(object):
positions = positions*nanometer
## The list of atom positions
self.positions = positions
def getTopology(self):
"""Get the Topology of the model."""
return self.topology
def getPositions(self):
"""Get the atomic positions."""
return self.positions
def add(self, addTopology, addPositions):
"""Add chains, residues, atoms, and bonds to the model.
Specify what to add by providing a new Topology object and the corresponding atomic positions.
All chains, residues, atoms, and bonds contained in the Topology are added to the model.
Parameters:
- addTopoology (Topology) a Topology whose contents should be added to the model
- addPositions (list) the positions of the atoms to add
"""
# Copy over the existing model.
newTopology = Topology()
newTopology.setUnitCellDimensions(deepcopy(self.topology.getUnitCellDimensions()))
newAtoms = {}
......@@ -106,7 +106,7 @@ class Modeller(object):
newPositions.append(deepcopy(self.positions[atom.index]))
for bond in self.topology.bonds():
newTopology.addBond(newAtoms[bond[0]], newAtoms[bond[1]])
# Add the new model
newAtoms = {}
......@@ -125,16 +125,16 @@ class Modeller(object):
def delete(self, toDelete):
"""Delete chains, residues, atoms, and bonds from the model.
You can specify objects to delete at any granularity: atoms, residues, or chains. Passing
in an Atom object causes that Atom to be deleted. Passing in a Residue object causes that
Residue and all Atoms it contains to be deleted. Passing in a Chain object causes that
Chain and all Residues and Atoms it contains to be deleted.
In all cases, when an Atom is deleted, any bonds it participates in are also deleted.
You also can specify a bond (as a tuple of Atom objects) to delete just that bond without
deleting the Atoms it connects.
Parameters:
- toDelete (list) a list of Atoms, Residues, Chains, and bonds (specified as tuples of Atoms) to delete
"""
......@@ -166,14 +166,14 @@ class Modeller(object):
newTopology.addBond(newAtoms[bond[0]], newAtoms[bond[1]])
self.topology = newTopology
self.positions = newPositions
def deleteWater(self):
"""Delete all water molecules from the model."""
self.delete(res for res in self.topology.residues() if res.name == "HOH")
def convertWater(self, model='tip3p'):
"""Convert all water molecules to a different water model.
Parameters:
- model (string='tip3p') the water model to convert to. Supported values are 'tip3p', 'spce', 'tip4pew', and 'tip5p'.
"""
......@@ -211,9 +211,9 @@ class Modeller(object):
newPositions.append(po)
newPositions.append(ph1)
newPositions.append(ph2)
# Add virtual sites.
if sites == 4:
newTopology.addAtom('M', None, newResidue)
newPositions.append(0.786646558*po + 0.106676721*ph1 + 0.106676721*ph2)
......@@ -239,19 +239,19 @@ class Modeller(object):
def addSolvent(self, forcefield, model='tip3p', boxSize=None, padding=None, positiveIon='Na+', negativeIon='Cl-', ionicStrength=0*molar):
"""Add solvent (both water and ions) to the model to fill a rectangular box.
The algorithm works as follows:
1. Water molecules are added to fill the box.
2. Water molecules are removed if their distance to any solute atom is less than the sum of their van der Waals radii.
3. If the solute is charged, enough positive or negative ions are added to neutralize it. Each ion is added by
randomly selecting a water molecule and replacing it with the ion.
4. Ion pairs are added to give the requested total ionic strength.
The box size can be specified in three ways. First, you can explicitly give a box size to use. Alternatively, you can
give a padding distance. The largest dimension of the solute (along the x, y, or z axis) is determined, and a cubic
box of size (largest dimension)+2*padding is used. Finally, if neither a box size nor a padding distance is specified,
the existing Topology's unit cell dimensions are used.
Parameters:
- forcefield (ForceField) the ForceField to use for determining van der Waals radii and atomic charges
- model (string='tip3p') the water model to use. Supported values are 'tip3p', 'spce', 'tip4pew', and 'tip5p'.
......@@ -264,7 +264,7 @@ class Modeller(object):
does not include ions that are added to neutralize the system.
"""
# Pick a unit cell size.
if boxSize is not None:
if is_quantity(boxSize):
boxSize = boxSize.value_in_unit(nanometer)
......@@ -278,9 +278,9 @@ class Modeller(object):
raise ValueError('Neither the box size nor padding was specified, and the Topology does not define unit cell dimensions')
box = box.value_in_unit(nanometer)
invBox = Vec3(1.0/box[0], 1.0/box[1], 1.0/box[2])
# Identify the ion types.
posIonElements = {'Cs+':elem.cesium, 'K+':elem.potassium, 'Li+':elem.lithium, 'Na+':elem.sodium, 'Rb+':elem.rubidium}
negIonElements = {'Cl-':elem.chlorine, 'Br-':elem.bromine, 'F-':elem.fluorine, 'I-':elem.iodine}
if positiveIon not in posIonElements:
......@@ -289,9 +289,9 @@ class Modeller(object):
raise ValueError('Illegal value for negative ion: %s' % negativeIon)
positiveElement = posIonElements[positiveIon]
negativeElement = negIonElements[negativeIon]
# Load the pre-equilibrated water box.
vdwRadiusPerSigma = 0.5612310241546864907
if model == 'tip3p':
waterRadius = 0.31507524065751241*vdwRadiusPerSigma
......@@ -308,9 +308,9 @@ class Modeller(object):
pdbPositions = pdb.getPositions().value_in_unit(nanometer)
pdbResidues = list(pdbTopology.residues())
pdbBoxSize = pdbTopology.getUnitCellDimensions().value_in_unit(nanometer)
# Have the ForceField build a System for the solute from which we can determine van der Waals radii.
system = forcefield.createSystem(self.topology)
nonbonded = None
for i in range(system.getNumForces()):
......@@ -324,7 +324,7 @@ class Modeller(object):
maxCutoff = waterCutoff
else:
maxCutoff = max(waterCutoff, max(cutoff))
# Copy the solute over.
newTopology = Topology()
......@@ -341,9 +341,9 @@ class Modeller(object):
newPositions.append(deepcopy(self.positions[atom.index]))
for bond in self.topology.bonds():
newTopology.addBond(newAtoms[bond[0]], newAtoms[bond[1]])
# Sort the solute atoms into cells for fast lookup.
if len(self.positions) == 0:
positions = []
else:
......@@ -357,9 +357,9 @@ class Modeller(object):
cells[cell].append(i)
else:
cells[cell] = [i]
# Create a generator that loops over atoms close to a position.
def neighbors(pos):
centralCell = tuple((int(floor(pos[i]/cellSize[i])) for i in range(3)))
offsets = (-1, 0, 1)
......@@ -370,16 +370,16 @@ class Modeller(object):
if cell in cells:
for atom in cells[cell]:
yield atom
# Define a function to compute the distance between two points, taking periodic boundary conditions into account.
def periodicDistance(pos1, pos2):
delta = pos1-pos2
delta = [delta[i]-floor(delta[i]*invBox[i]+0.5)*box[i] for i in range(3)]
return norm(delta)
# Find the list of water molecules to add.
newChain = newTopology.addChain()
if len(positions) == 0:
center = Vec3(0, 0, 0)
......@@ -397,18 +397,18 @@ class Modeller(object):
atomPos = pdbPositions[oxygen.index]+offset
if not any((atomPos[i] > box[i] for i in range(3))):
# This molecule is inside the box, so see how close to it is to the solute.
atomPos += center-box/2
for i in neighbors(atomPos):
if periodicDistance(atomPos, positions[i]) < cutoff[i]:
break
else:
# Record this water molecule as one to add.
addedWaters.append((residue.index, atomPos))
# There could be clashes between water molecules at the box edges. Find ones to remove.
upperCutoff = center+box/2-Vec3(waterCutoff, waterCutoff, waterCutoff)
lowerCutoff = center-box/2+Vec3(waterCutoff, waterCutoff, waterCutoff)
lowerSkinPositions = [pos for index, pos in addedWaters if pos[0] < lowerCutoff[0] or pos[1] < lowerCutoff[1] or pos[2] < lowerCutoff[2]]
......@@ -428,9 +428,9 @@ class Modeller(object):
if not any((periodicDistance(lowerSkinPositions[i], pos) < waterCutoff and norm(lowerSkinPositions[i]-pos) > waterCutoff for i in neighbors(pos))):
filteredWaters.append(entry)
addedWaters = filteredWaters
# Add ions to neutralize the system.
totalCharge = int(floor(0.5+sum((nonbonded.getParticleParameters(i)[0].value_in_unit(elementary_charge) for i in range(system.getNumParticles())))))
if abs(totalCharge) > len(addedWaters):
raise Exception('Cannot neutralize the system because the charge is greater than the number of available positions for ions')
......@@ -443,18 +443,18 @@ class Modeller(object):
del addedWaters[index]
for i in range(abs(totalCharge)):
addIon(positiveElement if totalCharge < 0 else negativeElement)
# Add ions based on the desired ionic strength.
numIons = len(addedWaters)*ionicStrength/(55.4*molar) # Pure water is about 55.4 molar (depending on temperature)
numPairs = int(floor(numIons/2+0.5))
for i in range(numPairs):
addIon(positiveElement)
for i in range(numPairs):
addIon(negativeElement)
# Add the water molecules.
for index, pos in addedWaters:
newResidue = newTopology.addResidue(residue.name, newChain)
residue = pdbResidues[index]
......@@ -472,14 +472,14 @@ class Modeller(object):
newTopology.setUnitCellDimensions(deepcopy(box)*nanometer)
self.topology = newTopology
self.positions = newPositions
class _ResidueData:
"""Inner class used to encapsulate data about the hydrogens for a residue."""
def __init__(self, name):
self.name = name
self.variants = []
self.hydrogens = []
class _Hydrogen:
"""Inner class used to encapsulate data about a hydrogen atom."""
def __init__(self, name, parent, maxph, variants, terminal):
......@@ -488,11 +488,11 @@ class Modeller(object):
self.maxph = maxph
self.variants = variants
self.terminal = terminal
@staticmethod
def loadHydrogenDefinitions(file):
"""Load an XML file containing definitions of hydrogens that should be added by addHydrogens().
The built in hydrogens.xml file containing definitions for standard amino acids and nucleotides is loaded automatically.
This method can be used to load additional definitions for other residue types. They will then be used in subsequent
calls to addHydrogens().
......@@ -516,45 +516,45 @@ class Modeller(object):
if 'terminal' in hydrogen.attrib:
terminal = hydrogen.attrib['terminal']
data.hydrogens.append(Modeller._Hydrogen(hydrogen.attrib['name'], hydrogen.attrib['parent'], maxph, atomVariants, terminal))
def addHydrogens(self, forcefield, pH=7.0, variants=None):
"""Add missing hydrogens to the model.
Some residues can exist in multiple forms depending on the pH and properties of the local environment. These
variants differ in the presence or absence of particular hydrogens. In particular, the following variants
are supported:
Aspartic acid:
ASH: Neutral form with a hydrogen on one of the delta oxygens
ASP: Negatively charged form without a hydrogen on either delta oxygen
Cysteine:
CYS: Neutral form with a hydrogen on the sulfur
CYX: No hydrogen on the sulfur (either negatively charged, or part of a disulfide bond)
Glutamic acid:
GLH: Neutral form with a hydrogen on one of the epsilon oxygens
GLU: Negatively charged form without a hydrogen on either epsilon oxygen
Histidine:
HID: Neutral form with a hydrogen on the ND1 atom
HIE: Neutral form with a hydrogen on the NE2 atom
HIP: Positively charged form with hydrogens on both ND1 and NE2
Lysine:
LYN: Neutral form with two hydrogens on the zeta nitrogen
LYS: Positively charged form with three hydrogens on the zeta nitrogen
The variant to use for each residue is determined by the following rules:
1. The most common variant at the specified pH is selected.
2. Any Cysteine that participates in a disulfide bond uses the CYX variant regardless of pH.
3. For a neutral Histidine residue, the HID or HIE variant is selected based on which one forms a better hydrogen bond.
You can override these rules by explicitly specifying a variant for any residue. Also keep in mind that this
function will only add hydrogens. It will never remove ones that are already present in the model, regardless
of the specified pH.
Definitions for standard amino acids and nucleotides are built in. You can call loadHydrogenDefinitions() to load
additional definitions for other residue types.
......@@ -567,7 +567,7 @@ class Modeller(object):
Returns: a list of what variant was actually selected for each residue, in the same format as the variants parameter
"""
# Check the list of variants.
residues = list(self.topology.residues())
if variants is not None:
if len(variants) != len(residues):
......@@ -575,23 +575,23 @@ class Modeller(object):
else:
variants = [None]*len(residues)
actualVariants = [None]*len(residues)
# Load the residue specifications.
if not Modeller._hasLoadedStandardHydrogens:
Modeller.loadHydrogenDefinitions(os.path.join(os.path.dirname(__file__), 'data', 'hydrogens.xml'))
# Make a list of atoms bonded to each atom.
bonded = {}
for atom in self.topology.atoms():
bonded[atom] = []
for atom1, atom2 in self.topology.bonds():
bonded[atom1].append(atom2)
bonded[atom2].append(atom1)
# Define a function that decides whether a set of atoms form a hydrogen bond, using fairly tolerant criteria.
def isHbond(d, h, a):
if norm(d-a) > 0.35*nanometer:
return False
......@@ -600,9 +600,9 @@ class Modeller(object):
deltaDH /= norm(deltaDH)
deltaHA /= norm(deltaHA)
return acos(dot(deltaDH, deltaHA)) < 50*degree
# Loop over residues.
newTopology = Topology()
newTopology.setUnitCellDimensions(deepcopy(self.topology.getUnitCellDimensions()))
newAtoms = {}
......@@ -617,19 +617,19 @@ class Modeller(object):
isCTerminal = (residue == chain._residues[-1])
if residue.name in Modeller._residueHydrogens:
# Add hydrogens. First select which variant to use.
spec = Modeller._residueHydrogens[residue.name]
variant = variants[residue.index]
if variant is None:
if residue.name == 'CYS':
# If this is part of a disulfide, use CYX.
sulfur = [atom for atom in residue.atoms() if atom.element == elem.sulfur]
if len(sulfur) == 1 and any((atom.residue != residue for atom in bonded[sulfur[0]])):
variant = 'CYX'
if residue.name == 'HIS' and pH > 6.5:
# See if either nitrogen already has a hydrogen attached.
nd1 = [atom for atom in residue.atoms() if atom.name == 'ND1']
ne2 = [atom for atom in residue.atoms() if atom.name == 'NE2']
if len(nd1) != 1 or len(ne2) != 1:
......@@ -646,7 +646,7 @@ class Modeller(object):
variant = 'HIE'
else:
# Estimate the hydrogen positions.
nd1Pos = self.positions[nd1.index]
ne2Pos = self.positions[ne2.index]
hd1Delta = Vec3(0, 0, 0)*nanometer
......@@ -661,7 +661,7 @@ class Modeller(object):
he2Pos = ne2Pos+he2Delta
# See whether either hydrogen would form a hydrogen bond.
nd1IsBonded = False
ne2IsBonded = False
for acceptor in acceptors:
......@@ -681,31 +681,31 @@ class Modeller(object):
if variant is not None and variant not in spec.variants:
raise ValueError('Illegal variant for %s residue: %s' % (residue.name, variant))
actualVariants[residue.index] = variant
# Make a list of hydrogens that should be present in the residue.
parents = [atom for atom in residue.atoms() if atom.element != elem.hydrogen]
parentNames = [atom.name for atom in parents]
hydrogens = [h for h in spec.hydrogens if (variant is None and pH <= h.maxph) or (h.variants is None and pH <= h.maxph) or (h.variants is not None and variant in h.variants)]
hydrogens = [h for h in hydrogens if h.terminal is None or (isNTerminal and h.terminal == 'N') or (isCTerminal and h.terminal == 'C')]
hydrogens = [h for h in hydrogens if h.parent in parentNames]
# Loop over atoms in the residue, adding them to the new topology along with required hydrogens.
for parent in residue.atoms():
# Add the atom.
newAtom = newTopology.addAtom(parent.name, parent.element, newResidue)
newAtoms[parent] = newAtom
newPositions.append(deepcopy(self.positions[parent.index]))
if parent in parents:
# Match expected hydrogens with existing ones and find which ones need to be added.
existing = [atom for atom in bonded[parent] if atom.element == elem.hydrogen]
expected = [h for h in hydrogens if h.parent == parent.name]
if len(existing) < len(expected):
# Try to match up existing hydrogens to expected ones.
matches = []
for e in existing:
match = [h for h in expected if h.name == e.name]
......@@ -714,16 +714,16 @@ class Modeller(object):
expected.remove(match[0])
else:
matches.append(None)
# If any hydrogens couldn't be matched by name, just match them arbitrarily.
for i in range(len(matches)):
if matches[i] is None:
matches[i] = expected[-1]
expected.remove(expected[-1])
# Add the missing hydrogens.
for h in expected:
newH = newTopology.addAtom(h.name, elem.hydrogen, newResidue)
newIndices.append(newH.index)
......@@ -741,7 +741,7 @@ class Modeller(object):
newTopology.addBond(newAtom, newH)
else:
# Just copy over the residue.
for atom in residue.atoms():
newAtom = newTopology.addAtom(atom.name, atom.element, newResidue)
newAtoms[atom] = newAtom
......@@ -749,9 +749,9 @@ class Modeller(object):
for bond in self.topology.bonds():
if bond[0] in newAtoms and bond[1] in newAtoms:
newTopology.addBond(newAtoms[bond[0]], newAtoms[bond[1]])
# The hydrogens were added at random positions. Now use the ForceField to fix them up.
system = forcefield.createSystem(newTopology, rigidWater=False)
atoms = list(newTopology.atoms())
for i in range(system.getNumParticles()):
......@@ -764,23 +764,23 @@ class Modeller(object):
self.topology = newTopology
self.positions = context.getState(getPositions=True).getPositions()
return actualVariants
def addExtraParticles(self, forcefield):
"""Add missing extra particles to the model that are required by a force field.
Some force fields use "extra particles" that do not represent actual atoms, but still need to be included in
the System. Examples include lone pairs, Drude particles, and the virtual sites used in some water models
to adjust the charge distribution. Extra particles can be recognized by the fact that their element is None.
This method is primarily used to add extra particles, but it can also remove them. It tries to match every
residue in the Topology to a template in the force field. If there is no match, it will both add and remove
extra particles as necessary to make it match.
Parameters:
- forcefield (ForceField) the ForceField defining what extra particles should be present
"""
# Create copies of all residue templates that have had all extra points removed.
templatesNoEP = {}
for resName, template in forcefield._templates.iteritems():
if any(atom.element is None for atom in template.atoms):
......@@ -801,9 +801,9 @@ class Modeller(object):
if b in newIndex:
newTemplate.externalBonds.append(newIndex[b])
templatesNoEP[template] = newTemplate
# Record which atoms are bonded to each other atom, with and without extra particles.
bondedToAtom = []
bondedToAtomNoEP = []
for atom in self.topology.atoms():
......@@ -815,18 +815,18 @@ class Modeller(object):
if atom1.element is not None and atom2.element is not None:
bondedToAtomNoEP[atom1.index].add(atom2.index)
bondedToAtomNoEP[atom2.index].add(atom1.index)
# If the force field has a DrudeForce, record the types of Drude particles and their parents since we'll
# need them for picking particle positions.
drudeTypeMap = {}
for force in forcefield._forces:
if isinstance(force, DrudeGenerator):
for type in force.typeMap:
drudeTypeMap[type] = force.typeMap[type][0]
# Create the new Topology.
newTopology = Topology()
newTopology.setUnitCellDimensions(deepcopy(self.topology.getUnitCellDimensions()))
newAtoms = {}
......@@ -835,9 +835,9 @@ class Modeller(object):
newChain = newTopology.addChain()
for residue in chain.residues():
newResidue = newTopology.addResidue(residue.name, newChain)
# Look for a matching template.
matchFound = False
signature = _createResidueSignature([atom.element for atom in residue.atoms()])
if signature in forcefield._templateSignatures:
......@@ -846,7 +846,7 @@ class Modeller(object):
matchFound = True
if matchFound:
# Just copy the residue over.
for atom in residue.atoms():
newAtom = newTopology.addAtom(atom.name, atom.element, newResidue)
newAtoms[atom] = newAtom
......@@ -854,7 +854,7 @@ class Modeller(object):
else:
# There's no matching template. Try to find one that matches based on everything except
# extra points.
template = None
residueNoEP = Residue(residue.name, residue.index, residue.chain)
residueNoEP._atoms = [atom for atom in residue.atoms() if atom.element is not None]
......@@ -874,16 +874,16 @@ class Modeller(object):
break
if template is None:
raise ValueError('Residue %d (%s) does not match any template defined by the ForceField.' % (residue.index+1, residue.name))
# Add the regular atoms.
for atom in residue.atoms():
if atom.element is not None:
newAtoms[atom] = newTopology.addAtom(atom.name, atom.element, newResidue)
newPositions.append(deepcopy(self.positions[atom.index]))
# Add the extra points.
templateAtomPositions = len(template.atoms)*[None]
for index, atom in enumerate(template.atoms):
if atom in matchingAtoms:
......@@ -895,7 +895,7 @@ class Modeller(object):
for site in template.virtualSites:
if site.index == index:
# This is a virtual site. Compute its position by the correct rule.
if site.type == 'average2':
position = site.weights[0]*templateAtomPositions[index+site.atoms[0]] + site.weights[1]*templateAtomPositions[index+site.atoms[1]]
elif site.type == 'average3':
......@@ -907,14 +907,14 @@ class Modeller(object):
position = templateAtomPositions[index+site.atoms[0]] + site.weights[0]*v1 + site.weights[1]*v2 + site.weights[2]*cross
if position is None and atom.type in drudeTypeMap:
# This is a Drude particle. Put it on top of its parent atom.
for atom2, pos in zip(template.atoms, templateAtomPositions):
if atom2.type in drudeTypeMap[atom.type]:
position = deepcopy(pos)
if position is None:
# We couldn't figure out the correct position. As a wild guess, just put it at the center of the residue
# and hope that energy minimization will fix it.
knownPositions = [x for x in templateAtomPositions if x is not None]
position = sum(knownPositions)/len(knownPositions)
newPositions.append(position*nanometer)
......@@ -922,4 +922,4 @@ class Modeller(object):
if bond[0] in newAtoms and bond[1] in newAtoms:
newTopology.addBond(newAtoms[bond[0]], newAtoms[bond[1]])
self.topology = newTopology
self.positions = newPositions
\ No newline at end of file
self.positions = newPositions
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012 Stanford University and the Authors.
Authors: Peter Eastman
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -48,28 +48,28 @@ except:
class PDBFile(object):
"""PDBFile parses a Protein Data Bank (PDB) file and constructs a Topology and a set of atom positions from it.
This class also provides methods for creating PDB files. To write a file containing a single model, call
writeFile(). You also can create files that contain multiple models. To do this, first call writeHeader(),
then writeModel() once for each model in the file, and finally writeFooter() to complete the file."""
_residueNameReplacements = {}
_atomNameReplacements = {}
def __init__(self, file):
"""Load a PDB file.
The atom positions and Topology can be retrieved by calling getPositions() and getTopology().
Parameters:
- file (string) the name of the file to load
"""
top = Topology()
## The Topology read from the PDB file
self.topology = top
# Load the PDB file
pdb = PdbStructure(open(file), load_all_models=True)
PDBFile._loadNameReplacementTables()
......@@ -95,7 +95,7 @@ class PDBFile(object):
element = atom.element
if element is None:
# Try to guess the element.
upper = atomName.upper()
if upper.startswith('CL'):
element = elem.chlorine
......@@ -133,9 +133,9 @@ class PDBFile(object):
self.topology.createStandardBonds()
self.topology.createDisulfideBonds(self.positions)
self._numpyPositions = None
# Add bonds based on CONECT records.
connectBonds = []
for connect in pdb.models[0].connects:
i = connect[0]
......@@ -152,14 +152,14 @@ class PDBFile(object):
def getTopology(self):
"""Get the Topology of the model."""
return self.topology
def getNumFrames(self):
"""Get the number of frames stored in the file."""
return len(self._positions)
def getPositions(self, asNumpy=False, frame=0):
"""Get the atomic positions.
Parameters:
- asNumpy (boolean=False) if true, the values are returned as a numpy array instead of a list of Vec3s
- frame (int=0) the index of the frame for which to get positions
......@@ -213,11 +213,11 @@ class PDBFile(object):
name = atom.attrib['name']
for id in atom.attrib:
map[atom.attrib[id]] = name
@staticmethod
def writeFile(topology, positions, file=sys.stdout, modelIndex=None):
"""Write a PDB file containing a single model.
Parameters:
- topology (Topology) The Topology defining the model to write
- positions (list) The list of atomic positions to write
......@@ -230,7 +230,7 @@ class PDBFile(object):
@staticmethod
def writeHeader(topology, file=sys.stdout):
"""Write out the header for a PDB file.
Parameters:
- topology (Topology) The Topology defining the molecular system being written
- file (file=stdout) A file to write the file to
......@@ -239,11 +239,11 @@ class PDBFile(object):
if boxSize is not None:
size = boxSize.value_in_unit(angstroms)
print >>file, "CRYST1%9.3f%9.3f%9.3f 90.00 90.00 90.00 P 1 1 " % size
@staticmethod
def writeModel(topology, positions, file=sys.stdout, modelIndex=None):
"""Write out a model to a PDB file.
Parameters:
- topology (Topology) The Topology defining the model to write
- positions (list) The list of atomic positions to write
......@@ -251,7 +251,7 @@ class PDBFile(object):
- modelIndex (int=None) If not None, the model will be surrounded by MODEL/ENDMDL records with this index
"""
if len(list(topology.atoms())) != len(positions):
raise ValueError('The number of positions must match the number of atoms')
raise ValueError('The number of positions must match the number of atoms')
if is_quantity(positions):
positions = positions.value_in_unit(angstroms)
if any(math.isnan(norm(pos)) for pos in positions):
......@@ -295,7 +295,7 @@ class PDBFile(object):
@staticmethod
def writeFooter(topology, file=sys.stdout):
"""Write out the footer for a PDB file.
Parameters:
- topology (Topology) The Topology defining the molecular system being written
- file (file=stdout) A file to write the file to
......
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012 Stanford University and the Authors.
Authors: Peter Eastman
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -33,16 +33,16 @@ __version__ = "1.0"
import simtk.openmm as mm
from simtk.openmm.app import PDBFile
class PDBReporter(object):
"""PDBReporter outputs a series of frames from a Simulation to a PDB file.
To use it, create a PDBReporter, then add it to the Simulation's list of reporters.
"""
def __init__(self, file, reportInterval):
"""Create a PDBReporter.
Parameters:
- file (string) The file to write to
- reportInterval (int) The interval (in time steps) at which to write frames
......@@ -51,10 +51,10 @@ class PDBReporter(object):
self._out = open(file, 'w')
self._topology = None
self._nextModel = 0
def describeNextReport(self, simulation):
"""Get information about the next report this object will generate.
Parameters:
- simulation (Simulation) The Simulation to generate a report for
Returns: A five element tuple. The first element is the number of steps until the
......@@ -63,10 +63,10 @@ class PDBReporter(object):
"""
steps = self._reportInterval - simulation.currentStep%self._reportInterval
return (steps, True, False, False, False)
def report(self, simulation, state):
"""Generate a report.
Parameters:
- simulation (Simulation) The Simulation to generate a report for
- state (State) The current state of the simulation
......@@ -77,7 +77,7 @@ class PDBReporter(object):
self._nextModel += 1
PDBFile.writeModel(simulation.topology, state.getPositions(), self._out, self._nextModel)
self._nextModel += 1
def __del__(self):
PDBFile.writeFooter(self._topology, self._out)
self._out.close()
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012 Stanford University and the Authors.
Authors: Peter Eastman
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -33,25 +33,25 @@ __version__ = "1.0"
import simtk.openmm as mm
import simtk.unit as unit
class Simulation(object):
"""Simulation provides a simplified API for running simulations with OpenMM and reporting results.
A Simulation ties together various objects used for running a simulation: a Topology, System,
Integrator, and Context. To use it, you provide the Topology, System, and Integrator, and it
creates the Context automatically.
Simulation also maintains a list of "reporter" objects that record or analyze data as the simulation
runs, such as writing coordinates to files or displaying structures on the screen. For example,
the following line will cause a file called "output.pdb" to be created, and a structure written to
it every 1000 time steps:
simulation.reporters.append(PDBReporter('output.pdb', 1000))
"""
def __init__(self, topology, system, integrator, platform=None, platformProperties=None):
"""Create a Simulation.
Parameters:
- topology (Topology) A Topology describing the the system to simulate
- system (System) The OpenMM System object to simulate
......@@ -77,17 +77,17 @@ class Simulation(object):
self.context = mm.Context(system, integrator, platform)
else:
self.context = mm.Context(system, integrator, platform, platformProperties)
def minimizeEnergy(self, tolerance=1*unit.kilojoule/unit.mole, maxIterations=0):
"""Perform a local energy minimization on the system.
Parameters:
- tolerance (energy=1*kilojoule/mole) The energy tolerance to which the system should be minimized
- maxIterations (int=0) The maximum number of iterations to perform. If this is 0, minimization is continued
until the results converge without regard to how many iterations it takes.
"""
mm.LocalEnergyMinimizer.minimize(self.context, tolerance, maxIterations)
def step(self, steps):
"""Advance the simulation by integrating a specified number of time steps."""
stepTo = self.currentStep+steps
......
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012-2013 Stanford University and the Authors.
Authors: Peter Eastman
Contributors: Robert McGibbon
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -35,7 +35,7 @@ import simtk.openmm as mm
import simtk.unit as unit
from simtk.openmm.app import PDBFile
import math
class StateDataReporter(object):
"""StateDataReporter outputs information about a simulation, such as energy and temperature, to a file.
......
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012-2013 Stanford University and the Authors.
Authors: Peter Eastman
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -37,18 +37,18 @@ from simtk.unit import nanometers, sqrt
class Topology(object):
"""Topology stores the topological information about a system.
The structure of a Topology object is similar to that of a PDB file. It consists of a set of Chains
(often but not always corresponding to polymer chains). Each Chain contains a set of Residues,
and each Residue contains a set of Atoms. In addition, the Topology stores a list of which atom
pairs are bonded to each other, and the dimensions of the crystallographic unit cell.
Atom and residue names should follow the PDB 3.0 nomenclature for all molecules for which one exists.
"""
_standardBonds = {}
_hasLoadedStandardBonds = False
def __init__(self):
"""Create a new Topology object"""
self._chains = []
......@@ -56,19 +56,19 @@ class Topology(object):
self._numAtoms = 0
self._bonds = []
self._unitCellDimensions = None
def addChain(self):
"""Create a new Chain and add it to the Topology.
Returns: the newly created Chain
"""
chain = Chain(len(self._chains), self)
self._chains.append(chain)
return chain
def addResidue(self, name, chain):
"""Create a new Residue and add it to the Topology.
Parameters:
- name (string) The name of the residue to add
- chain (Chain) The Chain to add it to
......@@ -78,10 +78,10 @@ class Topology(object):
self._numResidues += 1
chain._residues.append(residue)
return residue
def addAtom(self, name, element, residue):
"""Create a new Atom and add it to the Topology.
Parameters:
- name (string) The name of the atom to add
- element (Element) The element of the atom to add
......@@ -92,52 +92,52 @@ class Topology(object):
self._numAtoms += 1
residue._atoms.append(atom)
return atom
def addBond(self, atom1, atom2):
"""Create a new bond and add it to the Topology.
Parameters:
- atom1 (Atom) The first Atom connected by the bond
- atom2 (Atom) The second Atom connected by the bond
"""
self._bonds.append((atom1, atom2))
def chains(self):
"""Iterate over all Chains in the Topology."""
return iter(self._chains)
def residues(self):
"""Iterate over all Residues in the Topology."""
for chain in self._chains:
for residue in chain._residues:
yield residue
def atoms(self):
"""Iterate over all Atoms in the Topology."""
for chain in self._chains:
for residue in chain._residues:
for atom in residue._atoms:
yield atom
def bonds(self):
"""Iterate over all bonds (each represented as a tuple of two Atoms) in the Topology."""
return iter(self._bonds)
def getUnitCellDimensions(self):
"""Get the dimensions of the crystallographic unit cell.
The return value may be None if this Topology does not represent a periodic structure.
"""
return self._unitCellDimensions
def setUnitCellDimensions(self, dimensions):
"""Set the dimensions of the crystallographic unit cell."""
self._unitCellDimensions = dimensions
@staticmethod
def loadBondDefinitions(file):
"""Load an XML file containing definitions of bonds that should be used by createStandardBonds().
The built in residues.xml file containing definitions for standard amino acids and nucleotides is loaded automatically.
This method can be used to load additional definitions for other residue types. They will then be used in subsequent
calls to createStandardBonds(). This is a static method, so it affects subsequent calls on all Topology objects.
......@@ -149,31 +149,31 @@ class Topology(object):
bonds = []
Topology._standardBonds[residue.attrib['name']] = bonds
for bond in residue.findall('Bond'):
bonds.append((bond.attrib['from'], bond.attrib['to']))
bonds.append((bond.attrib['from'], bond.attrib['to']))
def createStandardBonds(self):
"""Create bonds based on the atom and residue names for all standard residue types.
Definitions for standard amino acids and nucleotides are built in. You can call loadBondDefinitions() to load
additional definitions for other residue types.
"""
if not Topology._hasLoadedStandardBonds:
# Load the standard bond definitions.
Topology.loadBondDefinitions(os.path.join(os.path.dirname(__file__), 'data', 'residues.xml'))
Topology._hasLoadedStandardBonds = True
for chain in self._chains:
# First build a map of atom names to atoms.
atomMaps = []
for residue in chain._residues:
atomMap = {}
atomMaps.append(atomMap)
for atom in residue._atoms:
atomMap[atom.name] = atom
# Loop over residues and construct bonds.
for i in range(len(chain._residues)):
name = chain._residues[i].name
if name in Topology._standardBonds:
......@@ -198,17 +198,17 @@ class Topology(object):
toAtom = bond[1]
if fromAtom in atomMaps[fromResidue] and toAtom in atomMaps[toResidue]:
self.addBond(atomMaps[fromResidue][fromAtom], atomMaps[toResidue][toAtom])
def createDisulfideBonds(self, positions):
"""Identify disulfide bonds based on proximity and add them to the Topology.
Parameters:
- positions (list) The list of atomic positions based on which to identify bonded atoms
"""
def isCyx(res):
names = [atom.name for atom in res._atoms]
return 'SG' in names and 'HG' not in names
cyx = [res for res in self.residues() if res.name == 'CYS' and isCyx(res)]
atomNames = [[atom.name for atom in res._atoms] for res in cyx]
for i in range(len(cyx)):
......@@ -231,7 +231,7 @@ class Chain(object):
## The Topology this Chain belongs to
self.topology = topology
self._residues = []
def residues(self):
"""Iterate over all Residues in the Chain."""
return iter(self._residues)
......@@ -253,14 +253,14 @@ class Residue(object):
## The Chain this Residue belongs to
self.chain = chain
self._atoms = []
def atoms(self):
"""Iterate over all Atoms in the Residue."""
return iter(self._atoms)
class Atom(object):
"""An Atom object represents a residue within a Topology."""
def __init__(self, name, element, index, residue):
"""Construct a new Atom. You should call addAtom() on the Topology instead of calling this directly."""
## The name of the Atom
......
......@@ -10,7 +10,7 @@ Portions copyright (c) 2012 Stanford University and the Authors.
Authors: Peter Eastman
Contributors:
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -35,43 +35,43 @@ import simtk.unit as unit
class Vec3(tuple):
"""Vec3 is a 3-element tuple that supports many math operations."""
def __new__(cls, x, y, z):
"""Create a new Vec3."""
return tuple.__new__(cls, (x, y, z))
def __add__(self, other):
"""Add two Vec3s."""
return Vec3(self[0]+other[0], self[1]+other[1], self[2]+other[2])
def __radd__(self, other):
"""Add two Vec3s."""
return Vec3(self[0]+other[0], self[1]+other[1], self[2]+other[2])
def __sub__(self, other):
"""Add two Vec3s."""
return Vec3(self[0]-other[0], self[1]-other[1], self[2]-other[2])
def __rsub__(self, other):
"""Add two Vec3s."""
return Vec3(other[0]-self[0], other[1]-self[1], other[2]-self[2])
def __mul__(self, other):
"""Multiply a Vec3 by a constant."""
if unit.is_unit(other):
return unit.Quantity(self, other)
return Vec3(other*self[0], other*self[1], other*self[2])
def __rmul__(self, other):
"""Multiply a Vec3 by a constant."""
if unit.is_unit(other):
return unit.Quantity(self, other)
return Vec3(other*self[0], other*self[1], other*self[2])
def __div__(self, other):
"""Divide a Vec3 by a constant."""
return Vec3(self[0]/other, self[1]/other, self[2]/other)
__truediv__ = __div__
def __deepcopy__(self, memo):
return Vec3(self[0], self[1], self[2])
......@@ -16,7 +16,7 @@ Portions copyright (c) 2012 Stanford University and the Authors.
Authors: Christopher M. Bruns
Contributors: Peter Eastman
Permission is hereby granted, free of charge, to any person obtaining a
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
......@@ -42,7 +42,7 @@ __version__ = "0.6"
class BaseDimension(object):
'''
A physical dimension such as length, mass, or temperature.
It is unlikely the user will need to create new ones.
'''
# Keep deterministic order of dimensions
......@@ -57,7 +57,7 @@ class BaseDimension(object):
'angle': 8,
}
_next_unused_index = 9
def __init__(self, name):
"""Create a new BaseDimension.
......@@ -70,17 +70,17 @@ class BaseDimension(object):
BaseDimension._index_by_name[name] = BaseDimension._next_unused_index
BaseDimension._next_unused_index += 1
self._index = BaseDimension._index_by_name[name]
def __lt__(self, other):
"""
The implicit order of BaseDimensions is the order in which they were created.
This method is used for using BaseDimensions as hash keys, and also affects
the order in which units appear in multi-dimensional Quantities.
Returns True if self < other, False otherwise.
"""
return self._index < other._index
def __hash__(self):
"""
Needed for using BaseDimensions as hash keys.
......
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