Commit a381a3ab authored by peastman's avatar peastman
Browse files

Merge branch 'master' into gayberne

parents 5ecc8e00 1f7866ad
...@@ -81,51 +81,51 @@ class CharmmCrdFile(object): ...@@ -81,51 +81,51 @@ class CharmmCrdFile(object):
def _parse(self, fname): def _parse(self, fname):
crdfile = open(fname, 'r') with open(fname, 'r') as crdfile:
line = crdfile.readline()
while len(line.strip()) == 0: # Skip whitespace, as a precaution
line = crdfile.readline()
intitle = True
while intitle:
self.title.append(line.strip())
line = crdfile.readline() line = crdfile.readline()
if len(line.strip()) == 0:
intitle = False while len(line.strip()) == 0: # Skip whitespace, as a precaution
elif line.strip()[0] != '*': line = crdfile.readline()
intitle = False
else: intitle = True
intitle = True
while intitle:
while len(line.strip()) == 0: # Skip whitespace self.title.append(line.strip())
line = crdfile.readline() line = crdfile.readline()
if len(line.strip()) == 0:
try: intitle = False
self.natom = int(line.strip().split()[0]) elif line.strip()[0] != '*':
intitle = False
for row in range(self.natom): else:
line = crdfile.readline().strip().split() intitle = True
self.atomno.append(int(line[0]))
self.resno.append(int(line[1])) while len(line.strip()) == 0: # Skip whitespace
self.resname.append(line[2]) line = crdfile.readline()
self.attype.append(line[3])
pos = Vec3(float(line[4]), float(line[5]), float(line[6])) try:
self.positions.append(pos) self.natom = int(line.strip().split()[0])
self.segid.append(line[7])
self.resid.append(int(line[8])) for row in range(self.natom):
self.weighting.append(float(line[9])) line = crdfile.readline().strip().split()
self.atomno.append(int(line[0]))
if self.natom != len(self.positions): self.resno.append(int(line[1]))
raise CharmmFileError("Error parsing CHARMM .crd file: %d " self.resname.append(line[2])
"atoms requires %d positions (not %d)" % self.attype.append(line[3])
(self.natom, self.natom, pos = Vec3(float(line[4]), float(line[5]), float(line[6]))
len(self.positions)) self.positions.append(pos)
) self.segid.append(line[7])
self.resid.append(int(line[8]))
except (ValueError, IndexError) as e: self.weighting.append(float(line[9]))
raise CharmmFileError('Error parsing CHARMM coordinate file')
if self.natom != len(self.positions):
raise CharmmFileError("Error parsing CHARMM .crd file: %d "
"atoms requires %d positions (not %d)" %
(self.natom, self.natom,
len(self.positions))
)
except (ValueError, IndexError) as e:
raise CharmmFileError('Error parsing CHARMM coordinate file')
# Apply units to the positions now. Do it this way to allow for # Apply units to the positions now. Do it this way to allow for
# (possible) numpy functionality in the future. # (possible) numpy functionality in the future.
......
...@@ -178,23 +178,23 @@ class CharmmPsfFile(object): ...@@ -178,23 +178,23 @@ class CharmmPsfFile(object):
if not os.path.exists(psf_name): if not os.path.exists(psf_name):
raise IOError('Could not find PSF file %s' % psf_name) raise IOError('Could not find PSF file %s' % psf_name)
# Open the PSF and read the first line. It must start with "PSF" # Open the PSF and read the first line. It must start with "PSF"
psf = open(psf_name, 'r') with open(psf_name, 'r') as psf:
line = psf.readline() line = psf.readline()
if not line.startswith('PSF'): if not line.startswith('PSF'):
raise CharmmPSFError('Unrecognized PSF file. First line is %s' % raise CharmmPSFError('Unrecognized PSF file. First line is %s' %
line.strip()) line.strip())
# Store the flags # Store the flags
psf_flags = line.split()[1:] psf_flags = line.split()[1:]
# Now get all of the sections and store them in a dict # Now get all of the sections and store them in a dict
psf.readline() psf.readline()
# Now get all of the sections # Now get all of the sections
psfsections = _ZeroDict() psfsections = _ZeroDict()
while True: while True:
try: try:
sec, ptr, data = CharmmPsfFile._parse_psf_section(psf) sec, ptr, data = CharmmPsfFile._parse_psf_section(psf)
except CharmmPsfEOF: except CharmmPsfEOF:
break break
psfsections[sec] = (ptr, data) psfsections[sec] = (ptr, data)
# store the title # store the title
title = psfsections['NTITLE'][1] title = psfsections['NTITLE'][1]
# Next is the number of atoms # Next is the number of atoms
...@@ -1217,6 +1217,7 @@ class CharmmPsfFile(object): ...@@ -1217,6 +1217,7 @@ class CharmmPsfFile(object):
else: else:
raise ValueError('Illegal nonbonded method for use with GBSA') raise ValueError('Illegal nonbonded method for use with GBSA')
gb.setForceGroup(self.GB_FORCE_GROUP) gb.setForceGroup(self.GB_FORCE_GROUP)
gb.finalize()
system.addForce(gb) system.addForce(gb)
force.setReactionFieldDielectric(1.0) # applies to NonbondedForce force.setReactionFieldDielectric(1.0) # applies to NonbondedForce
......
...@@ -88,8 +88,7 @@ class DCDReporter(object): ...@@ -88,8 +88,7 @@ class DCDReporter(object):
if self._dcd is None: if self._dcd is None:
self._dcd = DCDFile(self._out, simulation.topology, simulation.integrator.getStepSize(), 0, self._reportInterval) self._dcd = DCDFile(self._out, simulation.topology, simulation.integrator.getStepSize(), 0, self._reportInterval)
a,b,c = state.getPeriodicBoxVectors() self._dcd.writeModel(state.getPositions(), periodicBoxVectors=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): def __del__(self):
self._out.close() self._out.close()
...@@ -6,7 +6,7 @@ Simbios, the NIH National Center for Physics-Based Simulation of ...@@ -6,7 +6,7 @@ Simbios, the NIH National Center for Physics-Based Simulation of
Biological Structures at Stanford, funded under the NIH Roadmap for Biological Structures at Stanford, funded under the NIH Roadmap for
Medical Research, grant U54 GM072970. See https://simtk.org. Medical Research, grant U54 GM072970. See https://simtk.org.
Portions copyright (c) 2012-2015 Stanford University and the Authors. Portions copyright (c) 2012-2016 Stanford University and the Authors.
Authors: Peter Eastman, Mark Friedrichs Authors: Peter Eastman, Mark Friedrichs
Contributors: Contributors:
...@@ -38,6 +38,8 @@ import itertools ...@@ -38,6 +38,8 @@ import itertools
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
import math import math
from math import sqrt, cos from math import sqrt, cos
from copy import deepcopy
from heapq import heappush, heappop
import simtk.openmm as mm import simtk.openmm as mm
import simtk.unit as unit import simtk.unit as unit
from . import element as elem from . import element as elem
...@@ -115,6 +117,8 @@ class ForceField(object): ...@@ -115,6 +117,8 @@ class ForceField(object):
""" """
self._atomTypes = {} self._atomTypes = {}
self._templates = {} self._templates = {}
self._patches = {}
self._templatePatches = {}
self._templateSignatures = {None:[]} self._templateSignatures = {None:[]}
self._atomClasses = {'':set()} self._atomClasses = {'':set()}
self._forces = [] self._forces = []
...@@ -175,8 +179,8 @@ class ForceField(object): ...@@ -175,8 +179,8 @@ class ForceField(object):
for residue in tree.getroot().find('Residues').findall('Residue'): for residue in tree.getroot().find('Residues').findall('Residue'):
resName = residue.attrib['name'] resName = residue.attrib['name']
template = ForceField._TemplateData(resName) template = ForceField._TemplateData(resName)
if 'overload' in residue.attrib: if 'override' in residue.attrib:
template.overloadLevel = int(residue.attrib['overload']) template.overrideLevel = int(residue.attrib['override'])
atomIndices = {} atomIndices = {}
for atom in residue.findall('Atom'): for atom in residue.findall('Atom'):
params = {} params = {}
...@@ -201,8 +205,81 @@ class ForceField(object): ...@@ -201,8 +205,81 @@ class ForceField(object):
template.addExternalBondByName(bond.attrib['atomName']) template.addExternalBondByName(bond.attrib['atomName'])
else: else:
template.addExternalBond(int(bond.attrib['from'])) template.addExternalBond(int(bond.attrib['from']))
for patch in residue.findall('AllowPatch'):
patchName = patch.attrib['name']
if ':' in patchName:
colonIndex = patchName.find(':')
self.registerTemplatePatch(resName, patchName[:colonIndex], int(patchName[colonIndex+1:])-1)
else:
self.registerTemplatePatch(resName, patchName, 0)
self.registerResidueTemplate(template) self.registerResidueTemplate(template)
# Load the patch defintions.
for tree in trees:
if tree.getroot().find('Patches') is not None:
for patch in tree.getroot().find('Patches').findall('Patch'):
patchName = patch.attrib['name']
if 'residues' in patch.attrib:
numResidues = int(patch.attrib['residues'])
else:
numResidues = 1
patchData = ForceField._PatchData(patchName, numResidues)
allAtomNames = set()
for atom in patch.findall('AddAtom'):
params = {}
for key in atom.attrib:
if key not in ('name', 'type'):
params[key] = _convertParameterToNumber(atom.attrib[key])
atomName = atom.attrib['name']
if atomName in allAtomNames:
raise ValueError('Patch '+patchName+' contains multiple atoms named '+atomName)
allAtomNames.add(atomName)
atomDescription = ForceField._PatchAtomData(atomName)
typeName = atom.attrib['type']
patchData.addedAtoms[atomDescription.residue].append(ForceField._TemplateAtomData(atomDescription.name, typeName, self._atomTypes[typeName].element, params))
for atom in patch.findall('ChangeAtom'):
params = {}
for key in atom.attrib:
if key not in ('name', 'type'):
params[key] = _convertParameterToNumber(atom.attrib[key])
atomName = atom.attrib['name']
if atomName in allAtomNames:
raise ValueError('Patch '+patchName+' contains multiple atoms named '+atomName)
allAtomNames.add(atomName)
atomDescription = ForceField._PatchAtomData(atomName)
typeName = atom.attrib['type']
patchData.changedAtoms[atomDescription.residue].append(ForceField._TemplateAtomData(atomDescription.name, typeName, self._atomTypes[typeName].element, params))
for atom in patch.findall('RemoveAtom'):
atomName = atom.attrib['name']
if atomName in allAtomNames:
raise ValueError('Patch '+patchName+' contains multiple atoms named '+atomName)
allAtomNames.add(atomName)
atomDescription = ForceField._PatchAtomData(atomName)
patchData.deletedAtoms.append(atomDescription)
for bond in patch.findall('AddBond'):
atom1 = ForceField._PatchAtomData(bond.attrib['atomName1'])
atom2 = ForceField._PatchAtomData(bond.attrib['atomName2'])
patchData.addedBonds.append((atom1, atom2))
for bond in patch.findall('RemoveBond'):
atom1 = ForceField._PatchAtomData(bond.attrib['atomName1'])
atom2 = ForceField._PatchAtomData(bond.attrib['atomName2'])
patchData.deletedBonds.append((atom1, atom2))
for bond in patch.findall('AddExternalBond'):
atom = ForceField._PatchAtomData(bond.attrib['atomName'])
patchData.addedExternalBonds.append(atom)
for bond in patch.findall('RemoveExternalBond'):
atom = ForceField._PatchAtomData(bond.attrib['atomName'])
patchData.deletedExternalBonds.append(atom)
for residue in patch.findall('ApplyToResidue'):
name = residue.attrib['name']
if ':' in name:
colonIndex = name.find(':')
self.registerTemplatePatch(name[colonIndex+1:], patchName, int(name[:colonIndex])-1)
else:
self.registerTemplatePatch(name, patchName, 0)
self.registerPatch(patchData)
# Load force definitions # Load force definitions
for tree in trees: for tree in trees:
...@@ -247,31 +324,40 @@ class ForceField(object): ...@@ -247,31 +324,40 @@ class ForceField(object):
def registerResidueTemplate(self, template): def registerResidueTemplate(self, template):
"""Register a new residue template.""" """Register a new residue template."""
if template.name in self._templates:
# There is already a template with this name, so check the override levels.
existingTemplate = self._templates[template.name]
if template.overrideLevel < existingTemplate.overrideLevel:
# The existing one takes precedence, so just return.
return
if template.overrideLevel > existingTemplate.overrideLevel:
# We need to delete the existing template.
del self._templates[template.name]
existingSignature = _createResidueSignature([atom.element for atom in existingTemplate.atoms])
self._templateSignatures[existingSignature].remove(existingTemplate)
else:
raise ValueError('Residue template %s with the same override level %d already exists.' % (template.name, template.overrideLevel))
# Register the template.
self._templates[template.name] = template self._templates[template.name] = template
signature = _createResidueSignature([atom.element for atom in template.atoms]) signature = _createResidueSignature([atom.element for atom in template.atoms])
if signature in self._templateSignatures: if signature in self._templateSignatures:
registered = False self._templateSignatures[signature].append(template)
for regtemplate in self._templateSignatures[signature]:
if regtemplate.name == template.name:
if regtemplate.overloadLevel > template.overloadLevel:
# ok to break - this is done every time a template is
# registered so there can only be one already existing
# with same name at a time
registered = True
break
elif regtemplate.overloadLevel < template.overloadLevel:
self._templateSignatures[signature].remove(regtemplate)
self._templateSignatures[signature].append(template)
registered = True
else:
raise Exception('Residue template %s with the same overloadLevel %d already exists.' %
(template.name, template.overloadLevel)
)
if not registered:
self._templateSignatures[signature].append(template)
else: else:
self._templateSignatures[signature] = [template] self._templateSignatures[signature] = [template]
def registerPatch(self, patch):
"""Register a new patch that can be applied to templates."""
self._patches[patch.name] = patch
def registerTemplatePatch(self, residue, patch, patchResidueIndex):
"""Register that a particular patch can be used with a particular residue."""
if residue not in self._templatePatches:
self._templatePatches[residue] = []
self._templatePatches[residue].append((patch, patchResidueIndex))
def registerScript(self, script): def registerScript(self, script):
"""Register a new script to be executed after building the System.""" """Register a new script to be executed after building the System."""
self._scripts.append(script) self._scripts.append(script)
...@@ -396,11 +482,21 @@ class ForceField(object): ...@@ -396,11 +482,21 @@ class ForceField(object):
"""Add a constraint to the system, avoiding duplicate constraints.""" """Add a constraint to the system, avoiding duplicate constraints."""
key = (min(atom1, atom2), max(atom1, atom2)) key = (min(atom1, atom2), max(atom1, atom2))
if key in self.constraints: if key in self.constraints:
if self.constraints(key) != distance: if self.constraints[key] != distance:
raise ValueError('Two constraints were specified between atoms %d and %d with different distances' % (atom1, atom2)) raise ValueError('Two constraints were specified between atoms %d and %d with different distances' % (atom1, atom2))
else: else:
self.constraints[key] = distance self.constraints[key] = distance
system.addConstraint(atom1, atom2, distance) system.addConstraint(atom1, atom2, distance)
def recordMatchedAtomParameters(self, residue, template, matches):
"""Record parameters for atoms based on having matched a residue to a template."""
matchAtoms = dict(zip(matches, residue.atoms()))
for atom, match in zip(residue.atoms(), matches):
self.atomType[atom] = template.atoms[match].type
self.atomParameters[atom] = template.atoms[match].parameters
for site in template.virtualSites:
if match == site.index:
self.virtualSites[atom] = (site, [matchAtoms[i].index for i in site.atoms], matchAtoms[site.excludeWith].index)
class _TemplateData(object): class _TemplateData(object):
"""Inner class used to encapsulate data about a residue template definition.""" """Inner class used to encapsulate data about a residue template definition."""
...@@ -410,7 +506,7 @@ class ForceField(object): ...@@ -410,7 +506,7 @@ class ForceField(object):
self.virtualSites = [] self.virtualSites = []
self.bonds = [] self.bonds = []
self.externalBonds = [] self.externalBonds = []
self.overloadLevel = 0 self.overrideLevel = 0
def getAtomIndexByName(self, atom_name): def getAtomIndexByName(self, atom_name):
"""Look up an atom index by atom name, providing a helpful error message if not found.""" """Look up an atom index by atom name, providing a helpful error message if not found."""
...@@ -496,6 +592,92 @@ class ForceField(object): ...@@ -496,6 +592,92 @@ class ForceField(object):
else: else:
self.excludeWith = self.atoms[0] self.excludeWith = self.atoms[0]
class _PatchData(object):
"""Inner class used to encapsulate data about a patch definition."""
def __init__(self, name, numResidues):
self.name = name
self.numResidues = numResidues
self.addedAtoms = [[] for i in range(numResidues)]
self.changedAtoms = [[] for i in range(numResidues)]
self.deletedAtoms = []
self.addedBonds = []
self.deletedBonds = []
self.addedExternalBonds = []
self.deletedExternalBonds = []
def createPatchedTemplates(self, templates):
"""Apply this patch to a set of templates, creating new modified ones."""
if len(templates) != self.numResidues:
raise ValueError("Patch '%s' expected %d templates, received %d", (self.name, self.numResidues, len(templates)))
# Construct a new version of each template.
newTemplates = []
for index, template in enumerate(templates):
newTemplate = ForceField._TemplateData("%s-%s" % (template.name, self.name))
newTemplates.append(newTemplate)
# Build the list of atoms in it.
for atom in template.atoms:
if not any(deleted.name == atom.name and deleted.residue == index for deleted in self.deletedAtoms):
newTemplate.atoms.append(ForceField._TemplateAtomData(atom.name, atom.type, atom.element, atom.parameters))
for atom in self.addedAtoms[index]:
if any(a.name == atom.name for a in newTemplate.atoms):
raise ValueError("Patch '%s' adds an atom with the same name as an existing atom: %s" % (self.name, atom.name))
newTemplate.atoms.append(ForceField._TemplateAtomData(atom.name, atom.type, atom.element, atom.parameters))
oldAtomIndex = dict([(atom.name, i) for i, atom in enumerate(template.atoms)])
newAtomIndex = dict([(atom.name, i) for i, atom in enumerate(newTemplate.atoms)])
for atom in self.changedAtoms[index]:
if atom.name not in newAtomIndex:
raise ValueError("Patch '%s' modifies nonexistent atom '%s' in template '%s'" % (self.name, atom.name, template.name))
newTemplate.atoms[newAtomIndex[atom.name]] = ForceField._TemplateAtomData(atom.name, atom.type, atom.element, atom.parameters)
# Copy over the virtual sites, translating the atom indices.
indexMap = dict([(oldAtomIndex[name], newAtomIndex[name]) for name in newAtomIndex if name in oldAtomIndex])
for site in template.virtualSites:
if site.index in indexMap and all(i in indexMap for i in site.atoms):
newSite = deepcopy(site)
newSite.index = indexMap[site.index]
newSite.atoms = [indexMap[i] for i in site.atoms]
newTemplate.virtualSites.append(newSite)
# Build the lists of bonds and external bonds.
atomMap = dict([(template.atoms[i], indexMap[i]) for i in indexMap])
deletedBonds = [(atom1.name, atom2.name) for atom1, atom2 in self.deletedBonds if atom1.residue == index and atom2.residue == index]
for atom1, atom2 in template.bonds:
a1 = template.atoms[atom1]
a2 = template.atoms[atom2]
if a1 in atomMap and a2 in atomMap and (a1.name, a2.name) not in deletedBonds and (a2.name, a1.name) not in deletedBonds:
newTemplate.addBond(atomMap[a1], atomMap[a2])
deletedExternalBonds = [atom.name for atom in self.deletedExternalBonds if atom.residue == index]
for atom in template.externalBonds:
if template.atoms[atom].name not in deletedExternalBonds:
newTemplate.addExternalBond(indexMap[atom])
for atom1, atom2 in self.addedBonds:
if atom1.residue == index and atom2.residue == index:
newTemplate.addBondByName(atom1.name, atom2.name)
elif atom1.residue == index:
newTemplate.addExternalBondByName(atom1.name)
elif atom2.residue == index:
newTemplate.addExternalBondByName(atom2.name)
for atom in self.addedExternalBonds:
newTemplate.addExternalBondByName(atom.name)
return newTemplates
class _PatchAtomData(object):
"""Inner class used to encapsulate data about an atom in a patch definition."""
def __init__(self, description):
if ':' in description:
colonIndex = description.find(':')
self.residue = int(description[:colonIndex])-1
self.name = description[colonIndex+1:]
else:
self.residue = 0
self.name = description
class _AtomType(object): class _AtomType(object):
"""Inner class used to record atom types and associated properties.""" """Inner class used to record atom types and associated properties."""
def __init__(self, name, atomClass, mass, element): def __init__(self, name, atomClass, mass, element):
...@@ -575,7 +757,7 @@ class ForceField(object): ...@@ -575,7 +757,7 @@ class ForceField(object):
raise ValueError('%s: No parameters defined for atom type %s' % (self.forceName, t)) raise ValueError('%s: No parameters defined for atom type %s' % (self.forceName, t))
def _getResidueTemplateMatches(self, res, bondedToAtom): def _getResidueTemplateMatches(self, res, bondedToAtom, templateSignatures=None):
"""Return the residue template matches, or None if none are found. """Return the residue template matches, or None if none are found.
Parameters Parameters
...@@ -596,10 +778,12 @@ class ForceField(object): ...@@ -596,10 +778,12 @@ class ForceField(object):
""" """
template = None template = None
matches = None matches = None
if templateSignatures is None:
templateSignatures = self._templateSignatures
signature = _createResidueSignature([atom.element for atom in res.atoms()]) signature = _createResidueSignature([atom.element for atom in res.atoms()])
if signature in self._templateSignatures: if signature in templateSignatures:
allMatches = [] allMatches = []
for t in self._templateSignatures[signature]: for t in templateSignatures[signature]:
match = _matchResidue(res, t, bondedToAtom) match = _matchResidue(res, t, bondedToAtom)
if match is not None: if match is not None:
allMatches.append((t, match)) allMatches.append((t, match))
...@@ -805,8 +989,8 @@ class ForceField(object): ...@@ -805,8 +989,8 @@ class ForceField(object):
data.atomBonds[bond.atom2].append(i) data.atomBonds[bond.atom2].append(i)
# Find the template matching each residue and assign atom types. # Find the template matching each residue and assign atom types.
# If no templates are found, attempt to use residue template generators to create new templates (and potentially atom types/parameters).
unmatchedResidues = []
for chain in topology.chains(): for chain in topology.chains():
for res in chain.residues(): for res in chain.residues():
if res in residueTemplates: if res in residueTemplates:
...@@ -819,30 +1003,37 @@ class ForceField(object): ...@@ -819,30 +1003,37 @@ class ForceField(object):
# Attempt to match one of the existing templates. # Attempt to match one of the existing templates.
[template, matches] = self._getResidueTemplateMatches(res, bondedToAtom) [template, matches] = self._getResidueTemplateMatches(res, bondedToAtom)
if matches is None: if matches is None:
# No existing templates match. Try any registered residue template generators. unmatchedResidues.append(res)
for generator in self._templateGenerators: else:
if generator(self, res): data.recordMatchedAtomParameters(res, template, matches)
# This generator has registered a new residue template that should match.
[template, matches] = self._getResidueTemplateMatches(res, bondedToAtom) # Try to apply patches to find matches for any unmatched residues.
if matches is None:
# Something went wrong because the generated template does not match the residue signature. if len(unmatchedResidues) > 0:
raise Exception('The residue handler %s indicated it had correctly parameterized residue %s, but the generated template did not match the residue signature.' % (generator.__class__.__name__, str(res))) unmatchedResidues = _applyPatchesToMatchResidues(self, data, unmatchedResidues, bondedToAtom)
else:
# We successfully generated a residue template. Break out of the for loop. # If we still haven't found a match for a residue, attempt to use residue template generators to create
break # new templates (and potentially atom types/parameters).
# Raise an exception if we have found no templates that match. for res in unmatchedResidues:
if matches is None: # A template might have been generated on an earlier iteration of this loop.
raise ValueError('No template found for residue %d (%s). %s' % (res.index+1, res.name, _findMatchErrors(self, res))) [template, matches] = self._getResidueTemplateMatches(res, bondedToAtom)
if matches is None:
# Store parameters for the matched residue template. # Try all generators.
matchAtoms = dict(zip(matches, res.atoms())) for generator in self._templateGenerators:
for atom, match in zip(res.atoms(), matches): if generator(self, res):
data.atomType[atom] = template.atoms[match].type # This generator has registered a new residue template that should match.
data.atomParameters[atom] = template.atoms[match].parameters [template, matches] = self._getResidueTemplateMatches(res, bondedToAtom)
for site in template.virtualSites: if matches is None:
if match == site.index: # Something went wrong because the generated template does not match the residue signature.
data.virtualSites[atom] = (site, [matchAtoms[i].index for i in site.atoms], matchAtoms[site.excludeWith].index) raise Exception('The residue handler %s indicated it had correctly parameterized residue %s, but the generated template did not match the residue signature.' % (generator.__class__.__name__, str(res)))
else:
# We successfully generated a residue template. Break out of the for loop.
break
if matches is None:
raise ValueError('No template found for residue %d (%s). %s' % (res.index+1, res.name, _findMatchErrors(self, res)))
else:
data.recordMatchedAtomParameters(res, template, matches)
# Create the System and add atoms # Create the System and add atoms
...@@ -1008,6 +1199,32 @@ class ForceField(object): ...@@ -1008,6 +1199,32 @@ class ForceField(object):
return sys return sys
def _findBondsForExclusions(data, sys):
"""Create a list of bonds to use when identifying exclusions."""
bondIndices = []
for bond in data.bonds:
bondIndices.append((bond.atom1, bond.atom2))
# If a virtual site does *not* share exclusions with another atom, add a bond between it and its first parent atom.
for i in range(sys.getNumParticles()):
if sys.isVirtualSite(i):
(site, atoms, excludeWith) = data.virtualSites[data.atoms[i]]
if excludeWith is None:
bondIndices.append((i, site.getParticle(0)))
# Certain particles, such as lone pairs and Drude particles, share exclusions with a parent atom.
# If the parent atom does not interact with an atom, the child particle does not either.
for atom1, atom2 in bondIndices:
for child1 in data.excludeAtomWith[atom1]:
bondIndices.append((child1, atom2))
for child2 in data.excludeAtomWith[atom2]:
bondIndices.append((child1, child2))
for child2 in data.excludeAtomWith[atom2]:
bondIndices.append((atom1, child2))
return bondIndices
def _countResidueAtoms(elements): def _countResidueAtoms(elements):
"""Count the number of atoms of each element in a residue.""" """Count the number of atoms of each element in a residue."""
counts = {} counts = {}
...@@ -1054,15 +1271,14 @@ def _matchResidue(res, template, bondedToAtom): ...@@ -1054,15 +1271,14 @@ def _matchResidue(res, template, bondedToAtom):
corresponds to, or None if it does not match the template corresponds to, or None if it does not match the template
""" """
atoms = list(res.atoms()) atoms = list(res.atoms())
if len(atoms) != len(template.atoms): numAtoms = len(atoms)
if numAtoms != len(template.atoms):
return None return None
matches = len(atoms)*[0]
hasMatch = len(atoms)*[False]
# Translate from global to local atom indices, and record the bonds for each atom. # Translate from global to local atom indices, and record the bonds for each atom.
renumberAtoms = {} renumberAtoms = {}
for i in range(len(atoms)): for i in range(numAtoms):
renumberAtoms[atoms[i].index] = i renumberAtoms[atoms[i].index] = i
bondedTo = [] bondedTo = []
externalBonds = [] externalBonds = []
...@@ -1088,37 +1304,264 @@ def _matchResidue(res, template, bondedToAtom): ...@@ -1088,37 +1304,264 @@ def _matchResidue(res, template, bondedToAtom):
templateTypeCount[key] += 1 templateTypeCount[key] += 1
if residueTypeCount != templateTypeCount: if residueTypeCount != templateTypeCount:
return None return None
# Identify template atoms that could potentially be matches for each atom.
candidates = [[] for i in range(numAtoms)]
for i in range(numAtoms):
for j, atom in enumerate(template.atoms):
if (atom.element is not None and atom.element != atoms[i].element) or (atom.element is None and atom.name != atoms[i].name):
continue
if len(atom.bondedTo) != len(bondedTo[i]):
continue
if atom.externalBonds != externalBonds[i]:
continue
candidates[i].append(j)
# Find an optimal ordering for matching atoms. This means 1) start with the one that has the fewest options,
# and 2) follow with ones that are bonded to an already matched atom.
searchOrder = []
atomsToOrder = set(range(numAtoms))
efficientAtomSet = set()
efficientAtomHeap = []
while len(atomsToOrder) > 0:
if len(efficientAtomSet) == 0:
fewestNeighbors = numAtoms+1
for i in atomsToOrder:
if len(candidates[i]) < fewestNeighbors:
nextAtom = i
fewestNeighbors = len(candidates[i])
else:
nextAtom = heappop(efficientAtomHeap)[1]
efficientAtomSet.remove(nextAtom)
searchOrder.append(nextAtom)
atomsToOrder.remove(nextAtom)
for i in bondedTo[nextAtom]:
if i in atomsToOrder:
if i not in efficientAtomSet:
efficientAtomSet.add(i)
heappush(efficientAtomHeap, (len(candidates[i]), i))
inverseSearchOrder = [0]*numAtoms
for i in range(numAtoms):
inverseSearchOrder[searchOrder[i]] = i
bondedTo = [[inverseSearchOrder[bondedTo[i][j]] for j in range(len(bondedTo[i]))] for i in searchOrder]
candidates = [candidates[i] for i in searchOrder]
# Recursively match atoms. # Recursively match atoms.
if _findAtomMatches(atoms, template, bondedTo, externalBonds, matches, hasMatch, 0): matches = numAtoms*[0]
return matches hasMatch = numAtoms*[False]
if _findAtomMatches(template, bondedTo, matches, hasMatch, candidates, 0):
return [matches[inverseSearchOrder[i]] for i in range(numAtoms)]
return None return None
def _findAtomMatches(atoms, template, bondedTo, externalBonds, matches, hasMatch, position): def _getAtomMatchCandidates(template, bondedTo, matches, candidates, position):
"""Get a list of template atoms that are potential matches for the next atom."""
for bonded in bondedTo[position]:
if bonded < position:
# This atom is bonded to another one for which we already have a match, so only consider
# template atoms that *that* one is bonded to.
return template.atoms[matches[bonded]].bondedTo
return candidates[position]
def _findAtomMatches(template, bondedTo, matches, hasMatch, candidates, position):
"""This is called recursively from inside _matchResidue() to identify matching atoms.""" """This is called recursively from inside _matchResidue() to identify matching atoms."""
if position == len(atoms): if position == len(matches):
return True return True
elem = atoms[position].element for i in _getAtomMatchCandidates(template, bondedTo, matches, candidates, position):
name = atoms[position].name
for i in range(len(atoms)):
atom = template.atoms[i] atom = template.atoms[i]
if ((atom.element is not None and 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]: if not hasMatch[i] and i in candidates[position]:
# See if the bonds for this identification are consistent # See if the bonds for this identification are consistent
allBondsMatch = all((bonded > position or matches[bonded] in atom.bondedTo for bonded in bondedTo[position])) allBondsMatch = all((bonded > position or matches[bonded] in atom.bondedTo for bonded in bondedTo[position]))
if allBondsMatch: if allBondsMatch:
# This is a possible match, so trying matching the rest of the residue. # This is a possible match, so try matching the rest of the residue.
matches[position] = i matches[position] = i
hasMatch[i] = True hasMatch[i] = True
if _findAtomMatches(atoms, template, bondedTo, externalBonds, matches, hasMatch, position+1): if _findAtomMatches(template, bondedTo, matches, hasMatch, candidates, position+1):
return True return True
hasMatch[i] = False hasMatch[i] = False
return False return False
def _applyPatchesToMatchResidues(forcefield, data, residues, bondedToAtom):
"""Try to apply patches to find matches for residues."""
# Start by creating all templates than can be created by applying a combination of one-residue patches
# to a single template. The number of these is usually not too large, and they often cover a large fraction
# of residues.
patchedTemplateSignatures = {}
patchedTemplates = {}
for name, template in forcefield._templates.items():
if name in forcefield._templatePatches:
patches = [forcefield._patches[patchName] for patchName, patchResidueIndex in forcefield._templatePatches[name] if forcefield._patches[patchName].numResidues == 1]
if len(patches) > 0:
newTemplates = []
patchedTemplates[name] = newTemplates
_generatePatchedSingleResidueTemplates(template, patches, 0, newTemplates)
for patchedTemplate in newTemplates:
signature = _createResidueSignature([atom.element for atom in patchedTemplate.atoms])
if signature in patchedTemplateSignatures:
patchedTemplateSignatures[signature].append(patchedTemplate)
else:
patchedTemplateSignatures[signature] = [patchedTemplate]
# Now see if any of those templates matches any of the residues.
unmatchedResidues = []
for res in residues:
[template, matches] = forcefield._getResidueTemplateMatches(res, bondedToAtom, patchedTemplateSignatures)
if matches is None:
unmatchedResidues.append(res)
else:
data.recordMatchedAtomParameters(res, template, matches)
if len(unmatchedResidues) == 0:
return []
# We need to consider multi-residue patches. This can easily lead to a combinatorial explosion, so we make a simplifying
# assumption: that no residue is affected by more than one multi-residue patch (in addition to any number of single-residue
# patches). Record all multi-residue patches, and the templates they can be applied to.
patches = {}
maxPatchSize = 0
for patch in forcefield._patches.values():
if patch.numResidues > 1:
patches[patch.name] = [[] for i in range(patch.numResidues)]
maxPatchSize = max(maxPatchSize, patch.numResidues)
if maxPatchSize == 0:
return unmatchedResidues # There aren't any multi-residue patches
for templateName in forcefield._templatePatches:
for patchName, patchResidueIndex in forcefield._templatePatches[templateName]:
if patchName in patches:
# The patch should accept this template, *and* all patched versions of it generated above.
patches[patchName][patchResidueIndex].append(forcefield._templates[templateName])
if templateName in patchedTemplates:
patches[patchName][patchResidueIndex] += patchedTemplates[templateName]
# Record which unmatched residues are bonded to each other.
bonds = set()
topology = residues[0].chain.topology
for atom1, atom2 in topology.bonds():
if atom1.residue != atom2.residue:
res1 = atom1.residue
res2 = atom2.residue
if res1 in unmatchedResidues and res2 in unmatchedResidues:
bond = tuple(sorted((res1, res2), key=lambda x: x.index))
if bond not in bonds:
bonds.add(bond)
# Identify clusters of unmatched residues that are all bonded to each other. These are the ones we'll
# try to apply multi-residue patches to.
clusterSize = 2
clusters = bonds
while clusterSize <= maxPatchSize:
# Try to apply patches to clusters of this size.
for patchName in patches:
patch = forcefield._patches[patchName]
if patch.numResidues == clusterSize:
matchedClusters = _matchToMultiResiduePatchedTemplates(data, clusters, patch, patches[patchName], bondedToAtom)
for cluster in matchedClusters:
for residue in cluster:
unmatchedResidues.remove(residue)
bonds = set(bond for bond in bonds if bond[0] in unmatchedResidues and bond[1] in unmatchedResidues)
# Now extend the clusters to find ones of the next size up.
largerClusters = set()
for cluster in clusters:
for bond in bonds:
if bond[0] in cluster and bond[1] not in cluster:
newCluster = tuple(sorted(cluster+(bond[1],), key=lambda x: x.index))
largerClusters.add(newCluster)
elif bond[1] in cluster and bond[0] not in cluster:
newCluster = tuple(sorted(cluster+(bond[0],), key=lambda x: x.index))
largerClusters.add(newCluster)
if len(largerClusters) == 0:
# There are no clusters of this size or larger
break
clusters = largerClusters
clusterSize += 1
return unmatchedResidues
def _generatePatchedSingleResidueTemplates(template, patches, index, newTemplates):
"""Apply all possible combinations of a set of single-residue patches to a template."""
try:
patchedTemplate = patches[index].createPatchedTemplates([template])[0]
newTemplates.append(patchedTemplate)
except:
# This probably means the patch is inconsistent with another one that has already been applied,
# so just ignore it.
patchedTemplate = None
# Call this function recursively to generate combinations of patches.
if index+1 < len(patches):
_generatePatchedSingleResidueTemplates(template, patches, index+1, newTemplates)
if patchedTemplate is not None:
_generatePatchedSingleResidueTemplates(patchedTemplate, patches, index+1, newTemplates)
def _matchToMultiResiduePatchedTemplates(data, clusters, patch, residueTemplates, bondedToAtom):
"""Apply a multi-residue patch to templates, then try to match them against clusters of residues."""
matchedClusters = []
selectedTemplates = [None]*patch.numResidues
_applyMultiResiduePatch(data, clusters, patch, residueTemplates, selectedTemplates, 0, matchedClusters, bondedToAtom)
return matchedClusters
def _applyMultiResiduePatch(data, clusters, patch, candidateTemplates, selectedTemplates, index, matchedClusters, bondedToAtom):
"""This is called recursively to apply a multi-residue patch to all possible combinations of templates."""
if index < patch.numResidues:
for template in candidateTemplates[index]:
selectedTemplates[index] = template
_applyMultiResiduePatch(data, clusters, patch, candidateTemplates, selectedTemplates, index+1, matchedClusters, bondedToAtom)
else:
# We're at the deepest level of the recursion. We've selected a template for each residue, so apply the patch,
# then try to match it against clusters.
try:
patchedTemplates = patch.createPatchedTemplates(selectedTemplates)
except:
# This probably means the patch is inconsistent with another one that has already been applied,
# so just ignore it.
raise
return
newlyMatchedClusters = []
for cluster in clusters:
for residues in itertools.permutations(cluster):
residueMatches = []
for residue, template in zip(residues, patchedTemplates):
matches = _matchResidue(residue, template, bondedToAtom)
if matches is None:
residueMatches = None
break
else:
residueMatches.append(matches)
if residueMatches is not None:
# We successfully matched the template to the residues. Record the parameters.
for i in range(patch.numResidues):
data.recordMatchedAtomParameters(residues[i], patchedTemplates[i], residueMatches[i])
newlyMatchedClusters.append(cluster)
break
# Record which clusters were successfully matched.
matchedClusters += newlyMatchedClusters
for cluster in newlyMatchedClusters:
clusters.remove(cluster)
def _findMatchErrors(forcefield, res): def _findMatchErrors(forcefield, res):
"""Try to guess why a residue failed to match any template and return an error message.""" """Try to guess why a residue failed to match any template and return an error message."""
residueCounts = _countResidueAtoms([atom.element for atom in res.atoms()]) residueCounts = _countResidueAtoms([atom.element for atom in res.atoms()])
...@@ -1713,37 +2156,156 @@ class NonbondedGenerator(object): ...@@ -1713,37 +2156,156 @@ class NonbondedGenerator(object):
sys.addForce(force) sys.addForce(force)
def postprocessSystem(self, sys, data, args): def postprocessSystem(self, sys, data, args):
# Create exceptions based on bonds. # Create the exceptions.
bondIndices = [] bondIndices = _findBondsForExclusions(data, sys)
for bond in data.bonds: nonbonded = [f for f in sys.getForces() if isinstance(f, mm.NonbondedForce)][0]
bondIndices.append((bond.atom1, bond.atom2)) nonbonded.createExceptionsFromBonds(bondIndices, self.coulomb14scale, self.lj14scale)
# If a virtual site does *not* share exclusions with another atom, add a bond between it and its first parent atom. parsers["NonbondedForce"] = NonbondedGenerator.parseElement
for i in range(sys.getNumParticles()):
if sys.isVirtualSite(i):
(site, atoms, excludeWith) = data.virtualSites[data.atoms[i]]
if excludeWith is None:
bondIndices.append((i, site.getParticle(0)))
# Certain particles, such as lone pairs and Drude particles, share exclusions with a parent atom. ## @private
# If the parent atom does not interact with an atom, the child particle does not either. class LennardJonesGenerator(object):
"""A NBFix generator to construct the L-J force with NBFIX implemented as a lookup table"""
for atom1, atom2 in bondIndices: def __init__(self, forcefield, lj14scale):
for child1 in data.excludeAtomWith[atom1]: self.ff = forcefield
bondIndices.append((child1, atom2)) self.nbfixTypes = {}
for child2 in data.excludeAtomWith[atom2]: self.lj14scale = lj14scale
bondIndices.append((child1, child2)) self.ljTypes = ForceField._AtomTypeParameters(forcefield, 'LennardJonesForce', 'Atom', ('sigma', 'epsilon'))
for child2 in data.excludeAtomWith[atom2]:
bondIndices.append((atom1, child2))
# Create the exceptions. def registerNBFIX(self, parameters):
types = self.ff._findAtomTypes(parameters, 2)
if None not in types:
type1 = types[0][0]
type2 = types[1][0]
epsilon = _convertParameterToNumber(parameters['epsilon'])
sigma = _convertParameterToNumber(parameters['sigma'])
self.nbfixTypes[(type1, type2)] = [sigma, epsilon]
self.nbfixTypes[(type2, type1)] = [sigma, epsilon]
nonbonded = [f for f in sys.getForces() if isinstance(f, mm.NonbondedForce)][0] def registerLennardJones(self, parameters):
nonbonded.createExceptionsFromBonds(bondIndices, self.coulomb14scale, self.lj14scale) self.ljTypes.registerAtom(parameters)
parsers["NonbondedForce"] = NonbondedGenerator.parseElement @staticmethod
def parseElement(element, ff):
existing = [f for f in ff._forces if isinstance(f, LennardJonesGenerator)]
if len(existing) == 0:
generator = LennardJonesGenerator(ff, float(element.attrib['lj14scale']))
ff.registerGenerator(generator)
else:
# Multiple <LennardJonesForce> tags were found, probably in different files
generator = existing[0]
if abs(generator.lj14scale - float(element.attrib['lj14scale'])) > NonbondedGenerator.SCALETOL:
raise ValueError('Found multiple LennardJonesForce tags with different 1-4 scales')
for LJ in element.findall('Atom'):
generator.registerLennardJones(LJ.attrib)
for Nbfix in element.findall('NBFixPair'):
generator.registerNBFIX(Nbfix.attrib)
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
# First derive the lookup tables
nbfixTypeSet = set().union(*self.nbfixTypes)
ljIndexList = [None]*len(data.atoms)
numLjTypes = 0
ljTypeList = []
typeMap = {}
for i, atom in enumerate(data.atoms):
atype = data.atomType[atom]
values = tuple(self.ljTypes.getAtomParameters(atom, data))
if values in typeMap and atype not in nbfixTypeSet:
# Only non-NBFIX types can be compressed
ljIndexList[i] = typeMap[values]
else:
typeMap[values] = numLjTypes
ljIndexList[i] = numLjTypes
numLjTypes += 1
ljTypeList.append(atype)
reverseMap = [0]*len(typeMap)
for typeValue in typeMap:
reverseMap[typeMap[typeValue]] = typeValue
# Now everything is assigned. Create the A- and B-coefficient arrays
acoef = [0]*(numLjTypes*numLjTypes)
bcoef = acoef[:]
for m in range(numLjTypes):
for n in range(numLjTypes):
pair = (ljTypeList[m], ljTypeList[n])
if pair in self.nbfixTypes:
epsilon = self.nbfixTypes[pair][1]
sigma = self.nbfixTypes[pair][0]
sigma6 = sigma**6
acoef[m+numLjTypes*n] = 4*epsilon*sigma6*sigma6
bcoef[m+numLjTypes*n] = 4*epsilon*sigma6
continue
else:
sigma = 0.5*(reverseMap[m][0]+reverseMap[n][0])
sigma6 = sigma**6
epsilon = math.sqrt(reverseMap[m][-1]*reverseMap[n][-1])
acoef[m+numLjTypes*n] = 4*epsilon*sigma6*sigma6
bcoef[m+numLjTypes*n] = 4*epsilon*sigma6
self.force = mm.CustomNonbondedForce('acoef(type1, type2)/r^12 - bcoef(type1, type2)/r^6;')
self.force.addTabulatedFunction('acoef', mm.Discrete2DFunction(numLjTypes, numLjTypes, acoef))
self.force.addTabulatedFunction('bcoef', mm.Discrete2DFunction(numLjTypes, numLjTypes, bcoef))
self.force.addPerParticleParameter('type')
if nonbondedMethod in [CutoffPeriodic, Ewald, PME]:
self.force.setNonbondedMethod(mm.CustomNonbondedForce.CutoffPeriodic)
elif nonbondedMethod is NoCutoff:
self.force.setNonbondedMethod(mm.CustomNonbondedForce.NoCutoff)
elif nonbondedMethod is CutoffNonPeriodic:
self.force.setNonbondedMethod(mm.CustomNonbondedForce.CutoffNonPeriodic)
else:
raise AssertionError('Unrecognized nonbonded method [%s]' % nonbondedMethod)
# Add the particles
for i in ljIndexList:
self.force.addParticle((i,))
self.force.setUseLongRangeCorrection(True)
self.force.setCutoffDistance(nonbondedCutoff)
sys.addForce(self.force)
def postprocessSystem(self, sys, data, args):
# Create the exceptions.
bondIndices = _findBondsForExclusions(data, sys)
if self.lj14scale == 1:
# Just exclude the 1-2 and 1-3 interactions.
self.force.createExclusionsFromBonds(bondIndices, 2)
else:
forceCopy = deepcopy(self.force)
forceCopy.createExclusionsFromBonds(bondIndices, 2)
self.force.createExclusionsFromBonds(bondIndices, 3)
if self.force.getNumExclusions() > forceCopy.getNumExclusions() and self.lj14scale != 0:
# We need to create a CustomBondForce and use it to implement the scaled 1-4 interactions.
bonded = mm.CustomBondForce('%g*epsilon*((sigma/r)^12-(sigma/r)^6)' % (4*self.lj14scale))
bonded.addPerBondParameter('sigma')
bonded.addPerBondParameter('epsilon')
sys.addForce(bonded)
skip = set(tuple(forceCopy.getExclusionParticles(i)) for i in range(forceCopy.getNumExclusions()))
for i in range(self.force.getNumExclusions()):
p1,p2 = self.force.getExclusionParticles(i)
a1 = data.atoms[p1]
a2 = data.atoms[p2]
if (p1,p2) not in skip and (p2,p1) not in skip:
type1 = data.atomType[a1]
type2 = data.atomType[a2]
if (type1, type2) in self.nbfixTypes:
sigma, epsilon = self.nbfixTypes[(type1, type2)]
else:
values1 = self.ljTypes.getAtomParameters(a1, data)
values2 = self.ljTypes.getAtomParameters(a2, data)
sigma = 0.5*(values1[0]+values2[0])
epsilon = sqrt(values1[1]*values2[1])
bonded.addBond(p1, p2, (sigma, epsilon))
parsers["LennardJonesForce"] = LennardJonesGenerator.parseElement
## @private ## @private
...@@ -2060,33 +2622,9 @@ class CustomNonbondedGenerator(object): ...@@ -2060,33 +2622,9 @@ class CustomNonbondedGenerator(object):
sys.addForce(force) sys.addForce(force)
def postprocessSystem(self, sys, data, args): def postprocessSystem(self, sys, data, args):
# Create exclusions based on bonds.
bondIndices = []
for bond in data.bonds:
bondIndices.append((bond.atom1, bond.atom2))
# If a virtual site does *not* share exclusions with another atom, add a bond between it and its first parent atom.
for i in range(sys.getNumParticles()):
if sys.isVirtualSite(i):
(site, atoms, excludeWith) = data.virtualSites[data.atoms[i]]
if excludeWith is None:
bondIndices.append((i, site.getParticle(0)))
# Certain particles, such as lone pairs and Drude particles, share exclusions with a parent atom.
# If the parent atom does not interact with an atom, the child particle does not either.
for atom1, atom2 in bondIndices:
for child1 in data.excludeAtomWith[atom1]:
bondIndices.append((child1, atom2))
for child2 in data.excludeAtomWith[atom2]:
bondIndices.append((child1, child2))
for child2 in data.excludeAtomWith[atom2]:
bondIndices.append((atom1, child2))
# Create the exclusions. # Create the exclusions.
bondIndices = _findBondsForExclusions(data, sys)
nonbonded = [f for f in sys.getForces() if isinstance(f, mm.CustomNonbondedForce)][0] nonbonded = [f for f in sys.getForces() if isinstance(f, mm.CustomNonbondedForce)][0]
nonbonded.createExclusionsFromBonds(bondIndices, self.bondCutoff) nonbonded.createExclusionsFromBonds(bondIndices, self.bondCutoff)
...@@ -3504,33 +4042,36 @@ class AmoebaVdwGenerator(object): ...@@ -3504,33 +4042,36 @@ class AmoebaVdwGenerator(object):
# <Vdw class="1" sigma="0.371" epsilon="0.46024" reduction="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" /> # <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'], existing = [f for f in forceField._forces if isinstance(f, AmoebaVdwGenerator)]
if len(existing) == 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'])) float(element.attrib['vdw-13-scale']), float(element.attrib['vdw-14-scale']), float(element.attrib['vdw-15-scale']))
forceField._forces.append(generator) forceField.registerGenerator(generator)
generator.params = ForceField._AtomTypeParameters(forceField, 'AmoebaVdwForce', 'Vdw', ('sigma', 'epsilon', 'reduction')) generator.params = ForceField._AtomTypeParameters(forceField, 'AmoebaVdwForce', 'Vdw', ('sigma', 'epsilon', 'reduction'))
else:
# Multiple <AmoebaVdwForce> tags were found, probably in different files. Simply add more types to the existing one.
generator = existing[0]
if abs(generator.vdw13Scale - float(element.attrib['vdw-13-scale'])) > NonbondedGenerator.SCALETOL or \
abs(generator.vdw14Scale - float(element.attrib['vdw-14-scale'])) > NonbondedGenerator.SCALETOL or \
abs(generator.vdw15Scale - float(element.attrib['vdw-15-scale'])) > NonbondedGenerator.SCALETOL:
raise ValueError('Found multiple AmoebaVdwForce tags with different scale factors')
if generator.radiusrule != element.attrib['radiusrule'] or generator.epsilonrule != element.attrib['epsilonrule'] or \
generator.radiustype != element.attrib['radiustype'] or generator.radiussize != element.attrib['radiussize']:
raise ValueError('Found multiple AmoebaVdwForce tags with different combining rules')
generator.params.parseDefinitions(element) generator.params.parseDefinitions(element)
two_six = 1.122462048309372 two_six = 1.122462048309372
#============================================================================================= #=============================================================================================
# Return a set containing the indices of particles bonded to particle with index=particleIndex
#=============================================================================================
@staticmethod @staticmethod
def getBondedParticleSet(particleIndex, data): def getBondedParticleSets(sys, data):
bondedParticleSet = set()
for bond in data.atomBonds[particleIndex]:
atom1 = data.bonds[bond].atom1
atom2 = data.bonds[bond].atom2
if (atom1 != particleIndex):
bondedParticleSet.add(atom1)
else:
bondedParticleSet.add(atom2)
return bondedParticleSet bondedParticleSets = [set() for i in range(len(data.atoms))]
bondIndices = _findBondsForExclusions(data, sys)
for atom1, atom2 in bondIndices:
bondedParticleSets[atom1].add(atom2)
bondedParticleSets[atom2].add(atom1)
return bondedParticleSets
#============================================================================================= #=============================================================================================
...@@ -3539,53 +4080,45 @@ class AmoebaVdwGenerator(object): ...@@ -3539,53 +4080,45 @@ class AmoebaVdwGenerator(object):
sigmaMap = {'ARITHMETIC':1, 'GEOMETRIC':1, 'CUBIC-MEAN':1} sigmaMap = {'ARITHMETIC':1, 'GEOMETRIC':1, 'CUBIC-MEAN':1}
epsilonMap = {'ARITHMETIC':1, 'GEOMETRIC':1, 'HARMONIC':1, 'HHG':1} epsilonMap = {'ARITHMETIC':1, 'GEOMETRIC':1, 'HARMONIC':1, 'HHG':1}
# get or create force depending on whether it has already been added to the system force = mm.AmoebaVdwForce()
sys.addForce(force)
existing = [sys.getForce(i) for i in range(sys.getNumForces())]
existing = [f for f in existing if type(f) == mm.AmoebaVdwForce]
if len(existing) == 0:
force = mm.AmoebaVdwForce()
sys.addForce(force)
# sigma and epsilon combining rules # sigma and epsilon combining rules
if ('sigmaCombiningRule' in args): if ('sigmaCombiningRule' in args):
sigmaRule = args['sigmaCombiningRule'].upper() sigmaRule = args['sigmaCombiningRule'].upper()
if (sigmaRule.upper() in sigmaMap): if (sigmaRule.upper() in sigmaMap):
force.setSigmaCombiningRule(sigmaRule.upper()) force.setSigmaCombiningRule(sigmaRule.upper())
else:
stringList = ' ' . join(str(x) for x in sigmaMap.keys())
raise ValueError( "AmoebaVdwGenerator: sigma combining rule %s not recognized; valid values are %s; using default." % (sigmaRule, stringList) )
else: else:
force.setSigmaCombiningRule(self.radiusrule) stringList = ' ' . join(str(x) for x in sigmaMap.keys())
raise ValueError( "AmoebaVdwGenerator: sigma combining rule %s not recognized; valid values are %s; using default." % (sigmaRule, stringList) )
else:
force.setSigmaCombiningRule(self.radiusrule)
if ('epsilonCombiningRule' in args): if ('epsilonCombiningRule' in args):
epsilonRule = args['epsilonCombiningRule'].upper() epsilonRule = args['epsilonCombiningRule'].upper()
if (epsilonRule.upper() in epsilonMap): if (epsilonRule.upper() in epsilonMap):
force.setEpsilonCombiningRule(epsilonRule.upper()) 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) )
else: else:
force.setEpsilonCombiningRule(self.epsilonrule) 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) )
# cutoff else:
force.setEpsilonCombiningRule(self.epsilonrule)
if ('vdwCutoff' in args): # cutoff
force.setCutoff(args['vdwCutoff'])
else:
force.setCutoff(nonbondedCutoff)
# dispersion correction if ('vdwCutoff' in args):
force.setCutoff(args['vdwCutoff'])
else:
force.setCutoff(nonbondedCutoff)
if ('useDispersionCorrection' in args): # dispersion correction
force.setUseDispersionCorrection(bool(args['useDispersionCorrection']))
if (nonbondedMethod == PME): if ('useDispersionCorrection' in args):
force.setNonbondedMethod(mm.AmoebaVdwForce.CutoffPeriodic) force.setUseDispersionCorrection(bool(args['useDispersionCorrection']))
else: if (nonbondedMethod == PME):
force = existing[0] force.setNonbondedMethod(mm.AmoebaVdwForce.CutoffPeriodic)
# add particles to force # add particles to force
...@@ -3614,9 +4147,7 @@ class AmoebaVdwGenerator(object): ...@@ -3614,9 +4147,7 @@ class AmoebaVdwGenerator(object):
# (1) collect in bondedParticleSets[i], 1-2 indices for all bonded partners of particle i # (1) collect in bondedParticleSets[i], 1-2 indices for all bonded partners of particle i
# (2) add 1-2,1-3 and self to exclusion set # (2) add 1-2,1-3 and self to exclusion set
bondedParticleSets = [] bondedParticleSets = AmoebaVdwGenerator.getBondedParticleSets(sys, data)
for i in range(len(data.atoms)):
bondedParticleSets.append(AmoebaVdwGenerator.getBondedParticleSet(i, data))
for (i,atom) in enumerate(data.atoms): for (i,atom) in enumerate(data.atoms):
...@@ -3649,34 +4180,8 @@ class AmoebaMultipoleGenerator(object): ...@@ -3649,34 +4180,8 @@ class AmoebaMultipoleGenerator(object):
#============================================================================================= #=============================================================================================
def __init__(self, forceField, def __init__(self, forceField):
direct11Scale, direct12Scale, direct13Scale, direct14Scale,
mpole12Scale, mpole13Scale, mpole14Scale, mpole15Scale,
mutual11Scale, mutual12Scale, mutual13Scale, mutual14Scale,
polar12Scale, polar13Scale, polar14Scale, polar15Scale):
self.forceField = forceField self.forceField = forceField
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.mutual11Scale = mutual11Scale
self.mutual12Scale = mutual12Scale
self.mutual13Scale = mutual13Scale
self.mutual14Scale = mutual14Scale
self.polar12Scale = polar12Scale
self.polar13Scale = polar13Scale
self.polar14Scale = polar14Scale
self.polar15Scale = polar15Scale
self.typeMap = {} self.typeMap = {}
#============================================================================================= #=============================================================================================
...@@ -3734,30 +4239,13 @@ class AmoebaMultipoleGenerator(object): ...@@ -3734,30 +4239,13 @@ class AmoebaMultipoleGenerator(object):
# <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="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" /> # <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" />
generator = AmoebaMultipoleGenerator(forceField, existing = [f for f in forceField._forces if isinstance(f, AmoebaMultipoleGenerator)]
element.attrib['direct11Scale'], if len(existing) == 0:
element.attrib['direct12Scale'], generator = AmoebaMultipoleGenerator(forceField)
element.attrib['direct13Scale'], forceField.registerGenerator(generator)
element.attrib['direct14Scale'], else:
# Multiple <AmoebaMultipoleForce> tags were found, probably in different files. Simply add more types to the existing one.
element.attrib['mpole12Scale'], generator = existing[0]
element.attrib['mpole13Scale'],
element.attrib['mpole14Scale'],
element.attrib['mpole15Scale'],
element.attrib['mutual11Scale'],
element.attrib['mutual12Scale'],
element.attrib['mutual13Scale'],
element.attrib['mutual14Scale'],
element.attrib['polar12Scale'],
element.attrib['polar13Scale'],
element.attrib['polar14Scale'],
element.attrib['polar15Scale'])
forceField._forces.append(generator)
# set type map: [ kIndices, multipoles, AMOEBA/OpenMM axis type] # set type map: [ kIndices, multipoles, AMOEBA/OpenMM axis type]
...@@ -3988,45 +4476,37 @@ class AmoebaMultipoleGenerator(object): ...@@ -3988,45 +4476,37 @@ class AmoebaMultipoleGenerator(object):
methodMap = {NoCutoff:mm.AmoebaMultipoleForce.NoCutoff, methodMap = {NoCutoff:mm.AmoebaMultipoleForce.NoCutoff,
PME:mm.AmoebaMultipoleForce.PME} PME:mm.AmoebaMultipoleForce.PME}
# get or create force depending on whether it has already been added to the system force = mm.AmoebaMultipoleForce()
sys.addForce(force)
if (nonbondedMethod not in methodMap):
raise ValueError( "AmoebaMultipoleForce: input cutoff method not available." )
else:
force.setNonbondedMethod(methodMap[nonbondedMethod])
force.setCutoffDistance(nonbondedCutoff)
existing = [sys.getForce(i) for i in range(sys.getNumForces())] if ('ewaldErrorTolerance' in args):
existing = [f for f in existing if type(f) == mm.AmoebaMultipoleForce] force.setEwaldErrorTolerance(float(args['ewaldErrorTolerance']))
if len(existing) == 0:
force = mm.AmoebaMultipoleForce()
sys.addForce(force)
if (nonbondedMethod not in methodMap):
raise ValueError( "AmoebaMultipoleForce: input cutoff method not available." )
else:
force.setNonbondedMethod(methodMap[nonbondedMethod])
force.setCutoffDistance(nonbondedCutoff)
if ('ewaldErrorTolerance' in args):
force.setEwaldErrorTolerance(float(args['ewaldErrorTolerance']))
if ('polarization' in args):
polarizationType = args['polarization']
if (polarizationType.lower() == 'direct'):
force.setPolarizationType(mm.AmoebaMultipoleForce.Direct)
elif (polarizationType.lower() == 'extrapolated'):
force.setPolarizationType(mm.AmoebaMultipoleForce.Extrapolated)
else:
force.setPolarizationType(mm.AmoebaMultipoleForce.Mutual)
if ('aEwald' in args): if ('polarization' in args):
force.setAEwald(float(args['aEwald'])) polarizationType = args['polarization']
if (polarizationType.lower() == 'direct'):
force.setPolarizationType(mm.AmoebaMultipoleForce.Direct)
elif (polarizationType.lower() == 'extrapolated'):
force.setPolarizationType(mm.AmoebaMultipoleForce.Extrapolated)
else:
force.setPolarizationType(mm.AmoebaMultipoleForce.Mutual)
if ('pmeGridDimensions' in args): if ('aEwald' in args):
force.setPmeGridDimensions(args['pmeGridDimensions']) force.setAEwald(float(args['aEwald']))
if ('mutualInducedMaxIterations' in args): if ('pmeGridDimensions' in args):
force.setMutualInducedMaxIterations(int(args['mutualInducedMaxIterations'])) force.setPmeGridDimensions(args['pmeGridDimensions'])
if ('mutualInducedTargetEpsilon' in args): if ('mutualInducedMaxIterations' in args):
force.setMutualInducedTargetEpsilon(float(args['mutualInducedTargetEpsilon'])) force.setMutualInducedMaxIterations(int(args['mutualInducedMaxIterations']))
else: if ('mutualInducedTargetEpsilon' in args):
force = existing[0] force.setMutualInducedTargetEpsilon(float(args['mutualInducedTargetEpsilon']))
# add particles to force # add particles to force
# throw error if particle type not available # throw error if particle type not available
...@@ -4035,11 +4515,7 @@ class AmoebaMultipoleGenerator(object): ...@@ -4035,11 +4515,7 @@ class AmoebaMultipoleGenerator(object):
# 1-2 # 1-2
bonded12ParticleSets = [] bonded12ParticleSets = AmoebaVdwGenerator.getBondedParticleSets(sys, data)
for i in range(len(data.atoms)):
bonded12ParticleSet = AmoebaVdwGenerator.getBondedParticleSet(i, data)
bonded12ParticleSet = set(sorted(bonded12ParticleSet))
bonded12ParticleSets.append(bonded12ParticleSet)
# 1-3 # 1-3
...@@ -4627,11 +5103,7 @@ class AmoebaGeneralizedKirkwoodGenerator(object): ...@@ -4627,11 +5103,7 @@ class AmoebaGeneralizedKirkwoodGenerator(object):
# 1-2 # 1-2
bonded12ParticleSets = [] bonded12ParticleSets = AmoebaVdwGenerator.getBondedParticleSets(sys, data)
for i in range(len(data.atoms)):
bonded12ParticleSet = AmoebaVdwGenerator.getBondedParticleSet(i, data)
bonded12ParticleSet = set(sorted(bonded12ParticleSet))
bonded12ParticleSets.append(bonded12ParticleSet)
radiusType = 'Bondi' radiusType = 'Bondi'
for atomIndex in range(0, amoebaMultipoleForce.getNumMultipoles()): for atomIndex in range(0, amoebaMultipoleForce.getNumMultipoles()):
......
...@@ -1006,8 +1006,8 @@ def readAmberSystem(topology, prmtop_filename=None, prmtop_loader=None, shake=No ...@@ -1006,8 +1006,8 @@ def readAmberSystem(topology, prmtop_filename=None, prmtop_loader=None, shake=No
for i, (r, s) in enumerate(zip(radii, screen)): for i, (r, s) in enumerate(zip(radii, screen)):
if abs(r - gb_parms[i][0]) > 1e-4 or abs(s - gb_parms[i][1]) > 1e-4: if abs(r - gb_parms[i][0]) > 1e-4 or abs(s - gb_parms[i][1]) > 1e-4:
if not warned: if not warned:
warnings.warn('Non-optimal GB parameters detected for GB ' warnings.warn(
'model %s' % gbmodel) 'Non-optimal GB parameters detected for GB model %s' % gbmodel)
warned = True warned = True
gb_parms[i][0], gb_parms[i][1] = r, s gb_parms[i][0], gb_parms[i][1] = r, s
...@@ -1019,7 +1019,13 @@ def readAmberSystem(topology, prmtop_filename=None, prmtop_loader=None, shake=No ...@@ -1019,7 +1019,13 @@ def readAmberSystem(topology, prmtop_filename=None, prmtop_loader=None, shake=No
gb_parm[2], gb_parm[3], gb_parm[4]]) gb_parm[2], gb_parm[3], gb_parm[4]])
else: else:
gb.addParticle([charge, gb_parm[0], gb_parm[1]]) gb.addParticle([charge, gb_parm[0], gb_parm[1]])
# OBC2 with kappa == 0 uses mm.GBSAOBC2Force, which doesn't have
# a finalize method
if not (gbmodel == 'OBC2' and implicitSolventKappa == 0.):
gb.finalize()
system.addForce(gb) system.addForce(gb)
if nonbondedMethod == 'NoCutoff': if nonbondedMethod == 'NoCutoff':
gb.setNonbondedMethod(mm.NonbondedForce.NoCutoff) gb.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
elif nonbondedMethod == 'CutoffNonPeriodic': elif nonbondedMethod == 'CutoffNonPeriodic':
......
...@@ -6,9 +6,9 @@ Simbios, the NIH National Center for Physics-Based Simulation of ...@@ -6,9 +6,9 @@ Simbios, the NIH National Center for Physics-Based Simulation of
Biological Structures at Stanford, funded under the NIH Roadmap for Biological Structures at Stanford, funded under the NIH Roadmap for
Medical Research, grant U54 GM072970. See https://simtk.org. Medical Research, grant U54 GM072970. See https://simtk.org.
Portions copyright (c) 2012-2015 University of Virginia and the Authors. Portions copyright (c) 2012-2016 University of Virginia and the Authors.
Authors: Christoph Klein, Michael R. Shirts Authors: Christoph Klein, Michael R. Shirts
Contributors: Jason M. Swails, Peter Eastman Contributors: Jason M. Swails, Peter Eastman, Justin L. MacCallum
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"), copy of this software and associated documentation files (the "Software"),
...@@ -29,19 +29,25 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE ...@@ -29,19 +29,25 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE. USE OR OTHER DEALINGS IN THE SOFTWARE.
""" """
from __future__ import division, absolute_import from __future__ import division, absolute_import
from collections import defaultdict from collections import defaultdict
import copy import copy
from simtk.openmm.app import element as E from simtk.openmm.app import element as E
from simtk.openmm import CustomGBForce, Continuous2DFunction from simtk.openmm import CustomGBForce, Discrete2DFunction
import simtk.unit as u import simtk.unit as u
from math import floor
def strip_unit(value, unit): def strip_unit(value, unit):
"""Strip off any units and return value in unit"""
if not u.is_quantity(value): if not u.is_quantity(value):
return value return value
return value.value_in_unit(unit) return value.value_in_unit(unit)
# GBn and GBn2 lookup tables
d0=[2.26685,2.32548,2.38397,2.44235,2.50057,2.55867,2.61663,2.67444, 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, 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, 3.18854,3.24498,3.30132,3.35752,3.4136,
...@@ -106,8 +112,6 @@ d0=[2.26685,2.32548,2.38397,2.44235,2.50057,2.55867,2.61663,2.67444, ...@@ -106,8 +112,6 @@ d0=[2.26685,2.32548,2.38397,2.44235,2.50057,2.55867,2.61663,2.67444,
3.6629,3.71694,3.77095,3.82489,3.8788,3.93265,3.98646,4.04022, 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] 4.09395,4.14762,4.20126,4.25485,4.3084]
m0=[0.0381511,0.0338587,0.0301776,0.027003,0.0242506,0.0218529, 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.0197547,0.0179109,0.0162844,0.0148442,0.0135647,0.0124243,
0.0114047,0.0104906,0.00966876,0.008928,0.0082587,0.00765255, 0.0114047,0.0104906,0.00966876,0.008928,0.0082587,0.00765255,
...@@ -192,10 +196,10 @@ m0=[0.0381511,0.0338587,0.0301776,0.027003,0.0242506,0.0218529, ...@@ -192,10 +196,10 @@ m0=[0.0381511,0.0338587,0.0301776,0.027003,0.0242506,0.0218529,
0.0332033,0.030322,0.0277596,0.0254732,0.0234266,0.0215892, 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.0199351,0.018442,0.0170909,0.0158654,0.0147514,0.0137365,
0.0128101,0.0119627,0.0111863] 0.0128101,0.0119627,0.0111863]
# Rescale to nm # Rescale to nanometers
for i in range (len(d0)): d0 = [d / 10. for d in d0]
d0[i]=d0[i]/10 m0 = [m * 10. for m in m0]
m0[i]=m0[i]*10
def _get_bonded_atom_list(topology): def _get_bonded_atom_list(topology):
""" Returns a list of atoms bonded to each other atom in a dict """ """ Returns a list of atoms bonded to each other atom in a dict """
...@@ -205,6 +209,7 @@ def _get_bonded_atom_list(topology): ...@@ -205,6 +209,7 @@ def _get_bonded_atom_list(topology):
bondeds[a2].append(a1) bondeds[a2].append(a1)
return bondeds return bondeds
def _is_carboxylateO(atom, all_bonds): def _is_carboxylateO(atom, all_bonds):
if atom is not E.oxygen: return False if atom is not E.oxygen: return False
bondeds = all_bonds[atom] bondeds = all_bonds[atom]
...@@ -224,6 +229,7 @@ def _is_carboxylateO(atom, all_bonds): ...@@ -224,6 +229,7 @@ def _is_carboxylateO(atom, all_bonds):
# If we got here, must be a carboxylate # If we got here, must be a carboxylate
return True return True
def _bondi_radii(topology): def _bondi_radii(topology):
""" Sets the bondi radii """ """ Sets the bondi radii """
radii = [0.0 for atom in topology.atoms()] radii = [0.0 for atom in topology.atoms()]
...@@ -250,6 +256,7 @@ def _bondi_radii(topology): ...@@ -250,6 +256,7 @@ def _bondi_radii(topology):
radii[i] = 1.5 radii[i] = 1.5
return radii # converted to nanometers above return radii # converted to nanometers above
def _mbondi_radii(topology): def _mbondi_radii(topology):
""" Sets the mbondi radii """ """ Sets the mbondi radii """
radii = [0.0 for atom in topology.atoms()] radii = [0.0 for atom in topology.atoms()]
...@@ -286,6 +293,7 @@ def _mbondi_radii(topology): ...@@ -286,6 +293,7 @@ def _mbondi_radii(topology):
radii[i] = 1.5 radii[i] = 1.5
return radii # converted to nanometers above return radii # converted to nanometers above
def _mbondi2_radii(topology): def _mbondi2_radii(topology):
""" Sets the mbondi2 radii """ """ Sets the mbondi2 radii """
radii = [0.0 for atom in topology.atoms()] radii = [0.0 for atom in topology.atoms()]
...@@ -320,6 +328,7 @@ def _mbondi2_radii(topology): ...@@ -320,6 +328,7 @@ def _mbondi2_radii(topology):
radii[i] = 1.5 radii[i] = 1.5
return radii # Converted to nanometers above return radii # Converted to nanometers above
def _mbondi3_radii(topology): def _mbondi3_radii(topology):
""" Sets the mbondi3 radii """ """ Sets the mbondi3 radii """
radii = _mbondi2_radii(topology) radii = _mbondi2_radii(topology)
...@@ -333,9 +342,13 @@ def _mbondi3_radii(topology): ...@@ -333,9 +342,13 @@ def _mbondi3_radii(topology):
radii[i] = 1.17 radii[i] = 1.17
return radii # Converted to nanometers above return radii # Converted to nanometers above
def _createEnergyTerms(force, solventDielectric, soluteDielectric, SA, cutoff, kappa, offset): def _createEnergyTerms(force, solventDielectric, soluteDielectric, SA, cutoff, kappa, offset):
# Add the energy terms to the CustomGBForce. These are identical for all the GB models. """Add the energy terms to the CustomGBForce.
These are identical for all the GB models.
"""
params = "; solventDielectric=%.16g; soluteDielectric=%.16g; kappa=%.16g; offset=%.16g" % (solventDielectric, soluteDielectric, kappa, offset) params = "; solventDielectric=%.16g; soluteDielectric=%.16g; kappa=%.16g; offset=%.16g" % (solventDielectric, soluteDielectric, kappa, offset)
if cutoff is not None: if cutoff is not None:
params += "; cutoff=%.16g" % cutoff params += "; cutoff=%.16g" % cutoff
...@@ -367,6 +380,7 @@ def _createEnergyTerms(force, solventDielectric, soluteDielectric, SA, cutoff, k ...@@ -367,6 +380,7 @@ def _createEnergyTerms(force, solventDielectric, soluteDielectric, SA, cutoff, k
force.addEnergyTerm("-138.935485*(1/soluteDielectric-1/solventDielectric)*q1*q2*(1/f-"+str(1/cutoff)+");" force.addEnergyTerm("-138.935485*(1/soluteDielectric-1/solventDielectric)*q1*q2*(1/f-"+str(1/cutoff)+");"
"f=sqrt(r^2+B1*B2*exp(-r^2/(4*B1*B2)))"+params, CustomGBForce.ParticlePairNoExclusions) "f=sqrt(r^2+B1*B2*exp(-r^2/(4*B1*B2)))"+params, CustomGBForce.ParticlePairNoExclusions)
_SCREEN_PARAMETERS = { # normal, GBn, GBn2 _SCREEN_PARAMETERS = { # normal, GBn, GBn2
E.hydrogen : (0.85, 1.09085413633, 1.425952), E.hydrogen : (0.85, 1.09085413633, 1.425952),
E.carbon : (0.72, 0.48435382330, 1.058554), E.carbon : (0.72, 0.48435382330, 1.058554),
...@@ -377,33 +391,52 @@ _SCREEN_PARAMETERS = { # normal, GBn, GBn2 ...@@ -377,33 +391,52 @@ _SCREEN_PARAMETERS = { # normal, GBn, GBn2
E.sulfur : (0.96, 0.602256336067, -0.703469), E.sulfur : (0.96, 0.602256336067, -0.703469),
None : (0.8, 0.5, 0.5) # default None : (0.8, 0.5, 0.5) # default
} }
_SCREEN_PARAMETERS[E.deuterium] = _SCREEN_PARAMETERS[E.hydrogen] _SCREEN_PARAMETERS[E.deuterium] = _SCREEN_PARAMETERS[E.hydrogen]
def _screen_parameter(atom): def _screen_parameter(atom):
if atom.element in _SCREEN_PARAMETERS: if atom.element in _SCREEN_PARAMETERS:
return _SCREEN_PARAMETERS[atom.element] return _SCREEN_PARAMETERS[atom.element]
return _SCREEN_PARAMETERS[None] return _SCREEN_PARAMETERS[None]
class CustomAmberGBForce(CustomGBForce):
class CustomAmberGBForceBase(CustomGBForce):
"""Base class for all of the Amber custom GB forces.
Should not be instantiated directly, use one of its
derived classes instead.
"""
OFFSET = 0.009 OFFSET = 0.009
RADIUS_ARG_POSITION = 1 RADIUS_ARG_POSITION = 1
SCREEN_POSITION = 2 SCREEN_POSITION = 2
def __init__(self, *args, **kwargs): def __init__(self):
raise NotImplementedError('Cannot instantiate ABC') CustomGBForce.__init__(self)
self.parameters = []
def addParticle(self, params): def addParticle(self, params):
params = copy.deepcopy(params) """Add a particle to the force
params[self.RADIUS_ARG_POSITION] = strip_unit(params[self.RADIUS_ARG_POSITION], u.nanometer) - self.OFFSET
params[self.SCREEN_POSITION] *= params[self.RADIUS_ARG_POSITION] Particles are added in order. The number of particles
CustomGBForce.addParticle(self, params) added must match the number of particles in the system.
return params
Parameters
----------
params : list
A list of parameters to add to the force. The meaning
parameters depends on the model.
def setParticleParameters(self, idx, params): Returns
-------
list
The list of parameters after stripping off units and
modifying SCREEN.
"""
params = copy.deepcopy(params) params = copy.deepcopy(params)
params[self.RADIUS_ARG_POSITION] = strip_unit(params[self.RADIUS_ARG_POSITION], u.nanometer) - self.OFFSET params[self.RADIUS_ARG_POSITION] = strip_unit(params[self.RADIUS_ARG_POSITION], u.nanometer) - self.OFFSET
params[self.SCREEN_POSITION] *= params[self.RADIUS_ARG_POSITION] params[self.SCREEN_POSITION] *= params[self.RADIUS_ARG_POSITION]
CustomGBForce.addParticle(self, params) self.parameters.append(params)
return params return params
@staticmethod @staticmethod
...@@ -421,17 +454,50 @@ class CustomAmberGBForce(CustomGBForce): ...@@ -421,17 +454,50 @@ class CustomAmberGBForce(CustomGBForce):
List of all parameters needed for this GB model. These can be passed List of all parameters needed for this GB model. These can be passed
to addParticle or setParticleParameters after the charge is inserted to addParticle or setParticleParameters after the charge is inserted
at the beginning of the list at the beginning of the list
""" """
raise NotImplementedError('Must override getStandardParameters in subclasses') raise NotImplementedError(
'getStandardParameters must be defined in derived classes')
""" def finalize(self):
Amber Equivalent: igb = 1 """Finalize this force so it can be added to a system.
"""
class GBSAHCTForce(CustomAmberGBForce):
This method must be called before the force is added
to the system.
"""
self._addParticles()
def _addParticles(self):
for params in self.parameters:
CustomGBForce.addParticle(self, params)
class GBSAHCTForce(CustomAmberGBForceBase):
"""This class is equivalent to Amber ``igb=1``
The list of parameters to ``addParticle`` is: ``[q, or, sr]``.
Parameters
----------
solventDielectric: float
Dielectric constant for the solvent
soluteDielectric: float
Dielectric constant for the solute
SA: string or None
Surface area model to use
cutoff: float or Quantity or None
Cutoff distance to use. If float, value is in nm. If ``None``,
then no cutoffs are used.
kappa: float or Quantity
Debye kappa parameter related to modelling salt in GB. It has
units of 1 / length with 1 / nanometer assumed if a float
is given. A value of zero corresponds to zero salt concentration.
"""
def __init__(self, solventDielectric=78.5, soluteDielectric=1, SA=None, def __init__(self, solventDielectric=78.5, soluteDielectric=1, SA=None,
cutoff=None, kappa=0.0): cutoff=None, kappa=0.0):
CustomGBForce.__init__(self) CustomAmberGBForceBase.__init__(self)
self.addPerParticleParameter("q") self.addPerParticleParameter("q")
self.addPerParticleParameter("or") # Offset radius self.addPerParticleParameter("or") # Offset radius
...@@ -447,20 +513,53 @@ class GBSAHCTForce(CustomAmberGBForce): ...@@ -447,20 +513,53 @@ class GBSAHCTForce(CustomAmberGBForce):
@staticmethod @staticmethod
def getStandardParameters(topology): def getStandardParameters(topology):
""" Gets list of standard parameters for this GB model based on an input Topology
Parameters
----------
topology : simtk.openmm.app.Topology
Topology of the system to get parameters for
Returns
-------
list of float
List of all parameters needed for this GB model. These can be passed
to addParticle or setParticleParameters after the charge is inserted
at the beginning of the list
"""
radii = [[x/10] for x in _mbondi_radii(topology)] radii = [[x/10] for x in _mbondi_radii(topology)]
for i, atom in enumerate(topology.atoms()): for i, atom in enumerate(topology.atoms()):
radii[i].append(_screen_parameter(atom)[0]) radii[i].append(_screen_parameter(atom)[0])
return radii return radii
"""
Amber Equivalents: igb = 2
"""
class GBSAOBC1Force(CustomAmberGBForce):
class GBSAOBC1Force(CustomAmberGBForceBase):
"""This class is equivalent to Amber ``igb=2``
The list of parameters to ``addParticle`` is: ``[q, or, sr]``.
Parameters
----------
solventDielectric: float
Dielectric constant for the solvent
soluteDielectric: float
Dielectric constant for the solute
SA: string or None
Surface area model to use
cutoff: float or Quantity or None
Cutoff distance to use. If float, value is in nm. If ``None``,
then no cutoffs are used.
kappa: float or Quantity
Debye kappa parameter related to modelling salt in GB. It has
units of 1 / length with 1 / nanometer assumed if a float
is given. A value of zero corresponds to zero salt concentration.
"""
def __init__(self, solventDielectric=78.5, soluteDielectric=1, SA=None, def __init__(self, solventDielectric=78.5, soluteDielectric=1, SA=None,
cutoff=None, kappa=0.0): cutoff=None, kappa=0.0):
CustomGBForce.__init__(self) CustomAmberGBForceBase.__init__(self)
self.addPerParticleParameter("q") self.addPerParticleParameter("q")
self.addPerParticleParameter("or") # Offset radius self.addPerParticleParameter("or") # Offset radius
...@@ -476,20 +575,55 @@ class GBSAOBC1Force(CustomAmberGBForce): ...@@ -476,20 +575,55 @@ class GBSAOBC1Force(CustomAmberGBForce):
@staticmethod @staticmethod
def getStandardParameters(topology): def getStandardParameters(topology):
""" Gets list of standard parameters for this GB model based on an input Topology
Parameters
----------
topology : simtk.openmm.app.Topology
Topology of the system to get parameters for
Returns
-------
list of float
List of all parameters needed for this GB model. These can be passed
to addParticle or setParticleParameters after the charge is inserted
at the beginning of the list
"""
radii = [[x/10] for x in _mbondi2_radii(topology)] radii = [[x/10] for x in _mbondi2_radii(topology)]
for i, atom in enumerate(topology.atoms()): for i, atom in enumerate(topology.atoms()):
radii[i].append(_screen_parameter(atom)[0]) radii[i].append(_screen_parameter(atom)[0])
return radii return radii
"""
Amber Equivalents: igb = 5
"""
class GBSAOBC2Force(GBSAOBC1Force):
class GBSAOBC2Force(GBSAOBC1Force):
"""This class is equivalent to Amber ``igb=5``
The list of parameters to ``addParticle`` is: ``[q, or, sr]``.
Parameters
----------
solventDielectric: float
Dielectric constant for the solvent
soluteDielectric: float
Dielectric constant for the solute
SA: string or None
Surface area model to use
cutoff: float or Quantity or None
Cutoff distance to use. If float, value is in nm. If ``None``,
then no cutoffs are used.
kappa: float or Quantity
Debye kappa parameter related to modelling salt in GB. It has
units of 1 / length with 1 / nanometer assumed if a float
is given. A value of zero corresponds to zero salt concentration.
"""
def __init__(self, solventDielectric=78.5, soluteDielectric=1, SA=None, def __init__(self, solventDielectric=78.5, soluteDielectric=1, SA=None,
cutoff=None, kappa=0.0): cutoff=None, kappa=0.0):
CustomGBForce.__init__(self) # Note that this is not GBSAOBC1Force, as our initialization
# is different. We inherit for getStandardParameters.
CustomAmberGBForceBase.__init__(self)
self.addPerParticleParameter("q") self.addPerParticleParameter("q")
self.addPerParticleParameter("or") # Offset radius self.addPerParticleParameter("or") # Offset radius
...@@ -503,90 +637,185 @@ class GBSAOBC2Force(GBSAOBC1Force): ...@@ -503,90 +637,185 @@ class GBSAOBC2Force(GBSAOBC1Force):
"psi=I*or; radius=or+offset; offset=0.009", CustomGBForce.SingleParticle) "psi=I*or; radius=or+offset; offset=0.009", CustomGBForce.SingleParticle)
_createEnergyTerms(self, solventDielectric, soluteDielectric, SA, cutoff, kappa, 0.009) _createEnergyTerms(self, solventDielectric, soluteDielectric, SA, cutoff, kappa, 0.009)
"""
Amber Equivalents: igb = 7
"""
class GBSAGBnForce(CustomAmberGBForce):
def __init__(self, solventDielectric=78.5, soluteDielectric=1, SA=None,
cutoff=None, kappa=0.0):
CustomGBForce.__init__(self)
self.addPerParticleParameter("q")
self.addPerParticleParameter("or") # Offset radius
self.addPerParticleParameter("sr") # Scaled offset radius
self.addTabulatedFunction("getd0", Continuous2DFunction(21, 21, d0, 0.1, 0.2, 0.1, 0.2)) class GBSAGBnForce(CustomAmberGBForceBase):
self.addTabulatedFunction("getm0", Continuous2DFunction(21, 21, m0, 0.1, 0.2, 0.1, 0.2)) """This class is equivalent to Amber ``igb=7``
The list of parameters to ``addParticle`` is: ``[q, or, sr]``.
Parameters
----------
solventDielectric: float
Dielectric constant for the solvent
soluteDielectric: float
Dielectric constant for the solute
SA: string or None
Surface area model to use
cutoff: float or Quantity or None
Cutoff distance to use. If float, value is in nm. If ``None``,
then no cutoffs are used.
kappa: float or Quantity
Debye kappa parameter related to modelling salt in GB. It has
units of 1 / length with 1 / nanometer assumed if a float
is given. A value of zero corresponds to zero salt concentration.
"""
OFFSET = 0.009
self.addComputedValue("I", "Ivdw+neckScale*Ineck;" def __init__(self, solventDielectric=78.5, soluteDielectric=1, SA=None, cutoff=None, kappa=0.0):
"Ineck=step(radius1+radius2+neckCut-r)*getm0(radius1,radius2)/(1+100*(r-getd0(radius1,radius2))^2+0.3*1000000*(r-getd0(radius1,radius2))^6);"
"Ivdw=step(r+sr2-or1)*0.5*(1/L-1/U+0.25*(r-sr2^2/r)*(1/(U^2)-1/(L^2))+0.5*log(L/U)/r);"
"U=r+sr2;"
"L=max(or1, D);"
"D=abs(r-sr2);"
"radius1=or1+offset; radius2=or2+offset;"
"neckScale=0.361825; neckCut=0.68; offset=0.009", CustomGBForce.ParticlePairNoExclusions)
self.addComputedValue("B", "1/(1/or-tanh(1.09511284*psi-1.907992938*psi^2+2.50798245*psi^3)/radius);" CustomAmberGBForceBase.__init__(self)
"psi=I*or; radius=or+offset; offset=0.009", CustomGBForce.SingleParticle) self.solventDielectric = solventDielectric
_createEnergyTerms(self, solventDielectric, soluteDielectric, SA, cutoff, kappa, 0.009) self.soluteDielectric = soluteDielectric
self.SA = SA
self.cutoff = cutoff
self.kappa = kappa
self._uniqueRadii = None
self._radiusToIndex = None
def addParticle(self, parameters): def addParticle(self, parameters):
parameters = CustomAmberGBForce.addParticle(self, parameters) parameters = CustomAmberGBForceBase.addParticle(self, parameters)
if parameters[1] + self.OFFSET < 0.1 or parameters[1] + self.OFFSET > 0.2: if parameters[1] + self.OFFSET < 0.1 or parameters[1] + self.OFFSET > 0.2:
raise ValueError('Radii must be between 1 and 2 Angstroms for neck lookup') raise ValueError('Radii must be between 1 and 2 Angstroms for neck lookup')
def setParticleParameters(self, idx, parameters): def finalize(self):
parameters = CustomAmberGBForce.setParticleParameters(self, idx, parameters) self._findUniqueRadii()
if parameters[1] < 0.1 or parameters[1] > 0.2: self._createRadiusToIndexMap()
raise ValueError('Radii must be between 1 and 2 Angstroms for neck lookup') self._addEnergyTerms()
self._addParticles()
@staticmethod @staticmethod
def getStandardParameters(topology): def getStandardParameters(topology):
""" Gets list of standard parameters for this GB model based on an input Topology
Parameters
----------
topology : simtk.openmm.app.Topology
Topology of the system to get parameters for
Returns
-------
list of float
List of all parameters needed for this GB model. These can be passed
to addParticle or setParticleParameters after the charge is inserted
at the beginning of the list
"""
radii = [[x/10] for x in _bondi_radii(topology)] radii = [[x/10] for x in _bondi_radii(topology)]
for i, atom in enumerate(topology.atoms()): for i, atom in enumerate(topology.atoms()):
radii[i].append(_screen_parameter(atom)[1]) radii[i].append(_screen_parameter(atom)[1])
return radii return radii
""" def _findUniqueRadii(self):
Amber Equivalents: igb = 8 radii = [p[self.RADIUS_ARG_POSITION] for p in self.parameters]
""" self._uniqueRadii = list(sorted(set(radii)))
class GBSAGBn2Force(GBSAGBnForce):
def _createRadiusToIndexMap(self):
OFFSET = 0.0195141 self._radiusToIndex = {r: i for i, r in enumerate(self._uniqueRadii)}
def __init__(self, solventDielectric=78.5, soluteDielectric=1, SA=None, def _createUniqueTable(self, fullTable):
cutoff=None, kappa=0.0): tablePositions = [(r+self.OFFSET-0.1)*200 for r in self._uniqueRadii]
numRadii = len(self._uniqueRadii)
CustomGBForce.__init__(self) index1 = [0]*numRadii
index2 = [0]*numRadii
weight1 = [0]*numRadii
weight2 = [0]*numRadii
for i,p in enumerate(tablePositions):
if p <= 0:
weight1[i] = 1.0
elif p >= 20:
index1[i] = 20
weight1[i] = 1.0
else:
index1[i] = int(floor(p))
index2[i] = index1[i]+1
weight1[i] = index2[i]-p
weight2[i] = 1.0-weight1[i]
table = []
for i in range(numRadii):
for j in range(numRadii):
table.append(weight1[i]*weight1[j]*fullTable[index1[i]*21+index1[j]] +
weight1[i]*weight2[j]*fullTable[index1[i]*21+index2[j]] +
weight2[i]*weight1[j]*fullTable[index2[i]*21+index1[j]] +
weight2[i]*weight2[j]*fullTable[index2[i]*21+index2[j]])
return table
def _addParticles(self):
for p in self.parameters:
radIndex = self._radiusToIndex[p[self.RADIUS_ARG_POSITION]]
CustomGBForce.addParticle(self, p + [radIndex])
def _addEnergyTerms(self):
self.addPerParticleParameter("q") self.addPerParticleParameter("q")
self.addPerParticleParameter("or") # Offset radius self.addPerParticleParameter("or") # Offset radius
self.addPerParticleParameter("sr") # Scaled offset radius self.addPerParticleParameter("sr") # Scaled offset radius
self.addPerParticleParameter("alpha") self.addPerParticleParameter("radindex")
self.addPerParticleParameter("beta")
self.addPerParticleParameter("gamma") n = len(self._uniqueRadii)
m0Table = self._createUniqueTable(m0)
d0Table = self._createUniqueTable(d0)
self.addTabulatedFunction("getd0", Discrete2DFunction(n, n, d0Table))
self.addTabulatedFunction("getm0", Discrete2DFunction(n, n, m0Table))
self.addComputedValue("I", "Ivdw+neckScale*Ineck;"
"Ineck=step(radius1+radius2+neckCut-r)*getm0(radindex1,radindex2)/(1+100*(r-getd0(radindex1,radindex2))^2+"
"0.3*1000000*(r-getd0(radindex1,radindex2))^6);"
"Ivdw=select(step(r+sr2-or1), 0.5*(1/L-1/U+0.25*(r-sr2^2/r)*(1/(U^2)-1/(L^2))+0.5*log(L/U)/r), 0);"
"U=r+sr2;"
"L=max(or1, D);"
"D=abs(r-sr2);"
"radius1=or1+offset; radius2=or2+offset;"
"neckScale=0.361825; neckCut=0.68; offset=0.009", CustomGBForce.ParticlePairNoExclusions)
self.addTabulatedFunction("getd0", Continuous2DFunction(21, 21, d0, 0.1, 0.2, 0.1, 0.2)) self.addComputedValue("B", "1/(1/or-tanh(1.09511284*psi-1.907992938*psi^2+2.50798245*psi^3)/radius);"
self.addTabulatedFunction("getm0", Continuous2DFunction(21, 21, m0, 0.1, 0.2, 0.1, 0.2)) "psi=I*or; radius=or+offset; offset=0.009", CustomGBForce.SingleParticle)
_createEnergyTerms(self, self.solventDielectric, self.soluteDielectric, self.SA, self.cutoff, self.kappa, self.OFFSET)
self.addComputedValue("I", "Ivdw+neckScale*Ineck;"
"Ineck=step(radius1+radius2+neckCut-r)*getm0(radius1,radius2)/(1+100*(r-getd0(radius1,radius2))^2+0.3*1000000*(r-getd0(radius1,radius2))^6);"
"Ivdw=step(r+sr2-or1)*0.5*(1/L-1/U+0.25*(r-sr2^2/r)*(1/(U^2)-1/(L^2))+0.5*log(L/U)/r);"
"U=r+sr2;"
"L=max(or1, D);"
"D=abs(r-sr2);"
"radius1=or1+offset; radius2=or2+offset;"
"neckScale=0.826836; neckCut=0.68; offset=0.0195141", CustomGBForce.ParticlePairNoExclusions)
self.addComputedValue("B", "1/(1/or-tanh(alpha*psi-beta*psi^2+gamma*psi^3)/radius);" class GBSAGBn2Force(GBSAGBnForce):
"psi=I*or; radius=or+offset; offset=0.0195141", CustomGBForce.SingleParticle) """This class is equivalent to Amber ``igb=8``
_createEnergyTerms(self, solventDielectric, soluteDielectric, SA, cutoff, kappa, 0.0195141)
The list of parameters to ``addParticle`` is: ``[q, or, sr, alpha, beta, gamma]``.
Parameters
----------
solventDielectric: float
Dielectric constant for the solvent
soluteDielectric: float
Dielectric constant for the solute
SA: string or None
Surface area model to use
cutoff: float or Quantity or None
Cutoff distance to use. If float, value is in nm. If ``None``,
then no cutoffs are used.
kappa: float or Quantity
Debye kappa parameter related to modelling salt in GB. It has
units of 1 / length with 1 / nanometer assumed if a float
is given. A value of zero corresponds to zero salt concentration.
"""
OFFSET = 0.0195141
def __init__(self, solventDielectric=78.5, soluteDielectric=1, SA=None, cutoff=None, kappa=0.0):
GBSAGBnForce.__init__(self, solventDielectric, soluteDielectric, SA, cutoff, kappa)
@staticmethod @staticmethod
def getStandardParameters(topology): def getStandardParameters(topology):
""" Gets list of standard parameters for this GB model based on an input Topology
Parameters
----------
topology : simtk.openmm.app.Topology
Topology of the system to get parameters for
Returns
-------
list of float
List of all parameters needed for this GB model. These can be passed
to addParticle or setParticleParameters after the charge is inserted
at the beginning of the list
"""
radii = [[x/10] for x in _mbondi3_radii(topology)] radii = [[x/10] for x in _mbondi3_radii(topology)]
for i, atom in enumerate(topology.atoms()): for i, atom in enumerate(topology.atoms()):
radii[i].append(_screen_parameter(atom)[2]) radii[i].append(_screen_parameter(atom)[2])
...@@ -603,3 +832,32 @@ class GBSAGBn2Force(GBSAGBnForce): ...@@ -603,3 +832,32 @@ class GBSAGBn2Force(GBSAGBnForce):
else: else:
radii[i].extend([0.8, 4.85, 0.5]) radii[i].extend([0.8, 4.85, 0.5])
return radii return radii
def _addEnergyTerms(self):
self.addPerParticleParameter("q")
self.addPerParticleParameter("or") # Offset radius
self.addPerParticleParameter("sr") # Scaled offset radius
self.addPerParticleParameter("alpha")
self.addPerParticleParameter("beta")
self.addPerParticleParameter("gamma")
self.addPerParticleParameter("radindex")
n = len(self._uniqueRadii)
m0Table = self._createUniqueTable(m0)
d0Table = self._createUniqueTable(d0)
self.addTabulatedFunction("getd0", Discrete2DFunction(n, n, d0Table))
self.addTabulatedFunction("getm0", Discrete2DFunction(n, n, m0Table))
self.addComputedValue("I", "Ivdw+neckScale*Ineck;"
"Ineck=step(radius1+radius2+neckCut-r)*getm0(radindex1,radindex2)/(1+100*(r-getd0(radindex1,radindex2))^2+"
"0.3*1000000*(r-getd0(radindex1,radindex2))^6);"
"Ivdw=select(step(r+sr2-or1), 0.5*(1/L-1/U+0.25*(r-sr2^2/r)*(1/(U^2)-1/(L^2))+0.5*log(L/U)/r), 0);"
"U=r+sr2;"
"L=max(or1, D);"
"D=abs(r-sr2);"
"radius1=or1+offset; radius2=or2+offset;"
"neckScale=0.826836; neckCut=0.68; offset=0.0195141", CustomGBForce.ParticlePairNoExclusions)
self.addComputedValue("B", "1/(1/or-tanh(alpha*psi-beta*psi^2+gamma*psi^3)/radius);"
"psi=I*or; radius=or+offset; offset=0.0195141", CustomGBForce.SingleParticle)
_createEnergyTerms(self, self.solventDielectric, self.soluteDielectric, self.SA, self.cutoff, self.kappa, self.OFFSET)
...@@ -345,8 +345,11 @@ class Modeller(object): ...@@ -345,8 +345,11 @@ class Modeller(object):
elif padding is not None: elif padding is not None:
if is_quantity(padding): if is_quantity(padding):
padding = padding.value_in_unit(nanometer) padding = padding.value_in_unit(nanometer)
maxSize = max(max((pos[i] for pos in self.positions))-min((pos[i] for pos in self.positions)) for i in range(3)) if len(self.positions) == 0:
maxSize = maxSize.value_in_unit(nanometer) maxSize = 0
else:
maxSize = max(max((pos[i] for pos in self.positions))-min((pos[i] for pos in self.positions)) for i in range(3))
maxSize = maxSize.value_in_unit(nanometer)
box = (maxSize+2*padding)*Vec3(1, 1, 1) box = (maxSize+2*padding)*Vec3(1, 1, 1)
vectors = (Vec3(maxSize+2*padding, 0, 0), Vec3(0, maxSize+2*padding, 0), Vec3(0, 0, maxSize+2*padding)) vectors = (Vec3(maxSize+2*padding, 0, 0), Vec3(0, maxSize+2*padding, 0), Vec3(0, 0, maxSize+2*padding))
else: else:
......
...@@ -90,7 +90,7 @@ class MTSIntegrator(CustomIntegrator): ...@@ -90,7 +90,7 @@ class MTSIntegrator(CustomIntegrator):
def _createSubsteps(self, parentSubsteps, groups): def _createSubsteps(self, parentSubsteps, groups):
group, substeps = groups[0] group, substeps = groups[0]
stepsPerParentStep = substeps/parentSubsteps stepsPerParentStep = substeps//parentSubsteps
if stepsPerParentStep < 1 or stepsPerParentStep != int(stepsPerParentStep): if stepsPerParentStep < 1 or stepsPerParentStep != int(stepsPerParentStep):
raise ValueError("The number for substeps for each group must be a multiple of the number for the previous group") raise ValueError("The number for substeps for each group must be a multiple of the number for the previous group")
if group < 0 or group > 31: if group < 0 or group > 31:
......
...@@ -124,6 +124,8 @@ NO_OUTPUT_ARGS = [('LocalEnergyMinimizer', 'minimize', 'context'), ...@@ -124,6 +124,8 @@ NO_OUTPUT_ARGS = [('LocalEnergyMinimizer', 'minimize', 'context'),
('AmoebaMultipoleForce', 'setCovalentMap', 'covalentAtoms'), ('AmoebaMultipoleForce', 'setCovalentMap', 'covalentAtoms'),
('AmoebaMultipoleForce', 'getElectrostaticPotential', 'context'), ('AmoebaMultipoleForce', 'getElectrostaticPotential', 'context'),
('AmoebaMultipoleForce', 'getInducedDipoles', 'context'), ('AmoebaMultipoleForce', 'getInducedDipoles', 'context'),
('AmoebaMultipoleForce', 'getLabFramePermanentDipoles', 'context'),
('AmoebaMultipoleForce', 'getTotalDipoles', 'context'),
] ]
# SWIG assumes the target language shadow class owns the C++ class # SWIG assumes the target language shadow class owns the C++ class
...@@ -274,6 +276,8 @@ UNITS = { ...@@ -274,6 +276,8 @@ UNITS = {
#("AmoebaMultipoleForce", "getElectrostaticPotential") : ( ('unit.kilojoule_per_mole'), ()), #("AmoebaMultipoleForce", "getElectrostaticPotential") : ( ('unit.kilojoule_per_mole'), ()),
("AmoebaMultipoleForce", "getElectrostaticPotential") : ( None, ()), ("AmoebaMultipoleForce", "getElectrostaticPotential") : ( None, ()),
("AmoebaMultipoleForce", "getInducedDipoles") : ( None, ()), ("AmoebaMultipoleForce", "getInducedDipoles") : ( None, ()),
("AmoebaMultipoleForce", "getLabFramePermanentDipoles") : ( None, ()),
("AmoebaMultipoleForce", "getTotalDipoles") : ( None, ()),
("AmoebaMultipoleForce", "getSystemMultipoleMoments") : ( None, ()), ("AmoebaMultipoleForce", "getSystemMultipoleMoments") : ( None, ()),
("AmoebaOutOfPlaneBendForce", "getNumOutOfPlaneBends") : ( None, ()), ("AmoebaOutOfPlaneBendForce", "getNumOutOfPlaneBends") : ( None, ()),
......
...@@ -165,7 +165,8 @@ class TestCharmmFiles(unittest.TestCase): ...@@ -165,7 +165,8 @@ class TestCharmmFiles(unittest.TestCase):
#out = open('systems/ala-ala-ala-implicit-forces/'+file[i]+'.xml', 'w') #out = open('systems/ala-ala-ala-implicit-forces/'+file[i]+'.xml', 'w')
#out.write(XmlSerializer.serialize(state1)) #out.write(XmlSerializer.serialize(state1))
#out.close() #out.close()
state2 = XmlSerializer.deserialize(open('systems/ala-ala-ala-implicit-forces/'+file[i]+'.xml').read()) with open('systems/ala-ala-ala-implicit-forces/'+file[i]+'.xml') as xml:
state2 = XmlSerializer.deserialize(xml.read())
for f1, f2, in zip(state1.getForces().value_in_unit(kilojoules_per_mole/nanometer), state2.getForces().value_in_unit(kilojoules_per_mole/nanometer)): for f1, f2, in zip(state1.getForces().value_in_unit(kilojoules_per_mole/nanometer), state2.getForces().value_in_unit(kilojoules_per_mole/nanometer)):
diff = norm(f1-f2) diff = norm(f1-f2)
self.assertTrue(diff < 0.1 or diff/norm(f1) < 1e-4) self.assertTrue(diff < 0.1 or diff/norm(f1) < 1e-4)
......
...@@ -11,6 +11,7 @@ try: ...@@ -11,6 +11,7 @@ try:
except ImportError: except ImportError:
from io import StringIO from io import StringIO
import os import os
import warnings
class TestForceField(unittest.TestCase): class TestForceField(unittest.TestCase):
"""Test the ForceField.createSystem() method.""" """Test the ForceField.createSystem() method."""
...@@ -569,8 +570,8 @@ class TestForceField(unittest.TestCase): ...@@ -569,8 +570,8 @@ class TestForceField(unittest.TestCase):
# confirm charge # confirm charge
self.assertEqual(sys.getForce(0).getParticleParameters(0)[0]._value, 3.0) self.assertEqual(sys.getForce(0).getParticleParameters(0)[0]._value, 3.0)
def test_ResidueOverloading(self): def test_ResidueOverriding(self):
"""Test residue overloading via overload tag in the XML""" """Test residue overriding via override tag in the XML"""
ffxml1 = """<ForceField> ffxml1 = """<ForceField>
<AtomTypes> <AtomTypes>
...@@ -607,7 +608,7 @@ class TestForceField(unittest.TestCase): ...@@ -607,7 +608,7 @@ class TestForceField(unittest.TestCase):
<Type name="Fe2+_tip3p_standard" class="Fe2+_tip3p_standard" element="Fe" mass="55.85"/> <Type name="Fe2+_tip3p_standard" class="Fe2+_tip3p_standard" element="Fe" mass="55.85"/>
</AtomTypes> </AtomTypes>
<Residues> <Residues>
<Residue name="FE2" overload="1"> <Residue name="FE2" override="1">
<Atom name="FE2" type="Fe2+_tip3p_standard" charge="2.0"/> <Atom name="FE2" type="Fe2+_tip3p_standard" charge="2.0"/>
</Residue> </Residue>
</Residues> </Residues>
...@@ -625,6 +626,128 @@ class TestForceField(unittest.TestCase): ...@@ -625,6 +626,128 @@ class TestForceField(unittest.TestCase):
self.assertEqual(ff._templates['FE2'].atoms[0].type, 'Fe2+_tip3p_standard') self.assertEqual(ff._templates['FE2'].atoms[0].type, 'Fe2+_tip3p_standard')
ff.createSystem(pdb.topology) ff.createSystem(pdb.topology)
def test_LennardJonesGenerator(self):
""" Test the LennardJones generator"""
warnings.filterwarnings('ignore', category=CharmmPSFWarning)
psf = CharmmPsfFile('systems/ions.psf')
pdb = PDBFile('systems/ions.pdb')
params = CharmmParameterSet('systems/toppar_water_ions.str'
)
# Box dimensions (found from bounding box)
psf.setBox(12.009*angstroms, 12.338*angstroms, 11.510*angstroms)
# Turn off charges so we only test the Lennard-Jones energies
for a in psf.atom_list:
a.charge = 0.0
# Now compute the full energy
plat = Platform.getPlatformByName('Reference')
system = psf.createSystem(params, nonbondedMethod=PME,
nonbondedCutoff=5*angstroms)
con = Context(system, VerletIntegrator(2*femtoseconds), plat)
con.setPositions(pdb.positions)
# Now set up system from ffxml.
xml = """
<ForceField>
<AtomTypes>
<Type name="SOD" class="SOD" element="Na" mass="22.98977"/>
<Type name="CLA" class="CLA" element="Cl" mass="35.45"/>
</AtomTypes>
<Residues>
<Residue name="CLA">
<Atom name="CLA" type="CLA"/>
</Residue>
<Residue name="SOD">
<Atom name="SOD" type="SOD"/>
</Residue>
</Residues>
<LennardJonesForce lj14scale="1.0">
<Atom type="CLA" sigma="0.404468018036" epsilon="0.6276"/>
<Atom type="SOD" sigma="0.251367073323" epsilon="0.1962296"/>
<NBFixPair type1="CLA" type2="SOD" sigma="0.33239431" epsilon="0.350933"/>
</LennardJonesForce>
</ForceField> """
ff = ForceField(StringIO(xml))
system2 = ff.createSystem(pdb.topology, nonbondedMethod=PME,
nonbondedCutoff=5*angstroms)
con2 = Context(system2, VerletIntegrator(2*femtoseconds), plat)
con2.setPositions(pdb.positions)
state = con.getState(getEnergy=True, enforcePeriodicBox=True)
ene = state.getPotentialEnergy().value_in_unit(kilocalories_per_mole)
state2 = con2.getState(getEnergy=True, enforcePeriodicBox=True)
ene2 = state2.getPotentialEnergy().value_in_unit(kilocalories_per_mole)
self.assertAlmostEqual(ene, ene2)
def test_NBFix(self):
"""Test using LennardJonesGenerator to implement NBFix terms."""
# Create a chain of five atoms.
top = Topology()
chain = top.addChain()
res = top.addResidue('RES', chain)
top.addAtom('A', elem.oxygen, res)
top.addAtom('B', elem.carbon, res)
top.addAtom('C', elem.carbon, res)
top.addAtom('D', elem.carbon, res)
top.addAtom('E', elem.nitrogen, res)
atoms = list(top.atoms())
top.addBond(atoms[0], atoms[1])
top.addBond(atoms[1], atoms[2])
top.addBond(atoms[2], atoms[3])
top.addBond(atoms[3], atoms[4])
# Create the force field and system.
xml = """
<ForceField>
<AtomTypes>
<Type name="A" class="A" element="O" mass="1"/>
<Type name="B" class="B" element="C" mass="1"/>
<Type name="C" class="C" element="C" mass="1"/>
<Type name="D" class="D" element="C" mass="1"/>
<Type name="E" class="E" element="N" mass="1"/>
</AtomTypes>
<Residues>
<Residue name="RES">
<Atom name="A" type="A"/>
<Atom name="B" type="B"/>
<Atom name="C" type="C"/>
<Atom name="D" type="D"/>
<Atom name="E" type="E"/>
<Bond atomName1="A" atomName2="B"/>
<Bond atomName1="B" atomName2="C"/>
<Bond atomName1="C" atomName2="D"/>
<Bond atomName1="D" atomName2="E"/>
</Residue>
</Residues>
<LennardJonesForce lj14scale="0.3">
<Atom type="A" sigma="1" epsilon="0.1"/>
<Atom type="B" sigma="2" epsilon="0.2"/>
<Atom type="C" sigma="3" epsilon="0.3"/>
<Atom type="D" sigma="4" epsilon="0.4"/>
<Atom type="E" sigma="5" epsilon="0.5"/>
<NBFixPair type1="A" type2="D" sigma="2.5" epsilon="1.1"/>
<NBFixPair type1="A" type2="E" sigma="3.5" epsilon="1.5"/>
</LennardJonesForce>
</ForceField> """
ff = ForceField(StringIO(xml))
system = ff.createSystem(top)
# Check that it produces the correct energy.
integrator = VerletIntegrator(0.001)
context = Context(system, integrator, Platform.getPlatform(0))
positions = [Vec3(i, 0, 0) for i in range(5)]*nanometers
context.setPositions(positions)
def ljEnergy(sigma, epsilon, r):
return 4*epsilon*((sigma/r)**12-(sigma/r)**6)
expected = 0.3*ljEnergy(2.5, 1.1, 3) + 0.3*ljEnergy(3.5, sqrt(0.1), 3) + ljEnergy(3.5, 1.5, 4)
self.assertAlmostEqual(expected, context.getState(getEnergy=True).getPotentialEnergy().value_in_unit(kilojoules_per_mole))
class AmoebaTestForceField(unittest.TestCase): class AmoebaTestForceField(unittest.TestCase):
"""Test the ForceField.createSystem() method with the AMOEBA forcefield.""" """Test the ForceField.createSystem() method with the AMOEBA forcefield."""
......
import unittest
from simtk.openmm.app import *
from simtk.openmm import *
from simtk.unit import *
import simtk.openmm.app.forcefield as forcefield
import math
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
import os
class TestPatches(unittest.TestCase):
"""Test ForceFields that use patches."""
def testParsePatch(self):
"""Test parsing a <Patch> tag."""
xml = """
<ForceField>
<AtomTypes>
<Type name="A type" class="A class" element="O" mass="15.99943"/>
<Type name="B type" class="B class" element="H" mass="1.007947"/>
</AtomTypes>
<Patches>
<Patch name="Test">
<AddAtom name="A" type="A type"/>
<ChangeAtom name="B" type="B type"/>
<RemoveAtom name="C"/>
<AddBond atomName1="A" atomName2="B"/>
<RemoveBond atomName1="B" atomName2="C"/>
<AddExternalBond atomName="A"/>
<RemoveExternalBond atomName="C"/>
<ApplyToResidue name="RES"/>
</Patch>
</Patches>
</ForceField>"""
ff = ForceField(StringIO(xml))
self.assertEqual(1, len(ff._patches))
patch = ff._patches['Test']
self.assertEqual(1, len(patch.addedAtoms))
self.assertEqual(1, len(patch.addedAtoms[0]))
self.assertEqual(1, len(patch.changedAtoms))
self.assertEqual(1, len(patch.changedAtoms[0]))
self.assertEqual(1, len(patch.deletedAtoms))
self.assertEqual(1, len(patch.addedBonds))
self.assertEqual(1, len(patch.deletedBonds))
self.assertEqual(1, len(patch.addedExternalBonds))
self.assertEqual(1, len(patch.deletedExternalBonds))
self.assertEqual(1, len(ff._templatePatches))
self.assertEqual(1, len(ff._templatePatches['RES']))
self.assertEqual('A', patch.addedAtoms[0][0].name)
self.assertEqual('A type', patch.addedAtoms[0][0].type)
self.assertEqual('B', patch.changedAtoms[0][0].name)
self.assertEqual('B type', patch.changedAtoms[0][0].type)
self.assertEqual('C', patch.deletedAtoms[0].name)
self.assertEqual(0, patch.deletedAtoms[0].residue)
self.assertEqual('A', patch.addedBonds[0][0].name)
self.assertEqual(0, patch.addedBonds[0][0].residue)
self.assertEqual('B', patch.addedBonds[0][1].name)
self.assertEqual(0, patch.addedBonds[0][1].residue)
self.assertEqual('B', patch.deletedBonds[0][0].name)
self.assertEqual(0, patch.deletedBonds[0][0].residue)
self.assertEqual('C', patch.deletedBonds[0][1].name)
self.assertEqual(0, patch.deletedBonds[0][1].residue)
self.assertEqual('A', patch.addedExternalBonds[0].name)
self.assertEqual(0, patch.addedExternalBonds[0].residue)
self.assertEqual('C', patch.deletedExternalBonds[0].name)
self.assertEqual(0, patch.deletedExternalBonds[0].residue)
self.assertEqual('Test', ff._templatePatches['RES'][0][0])
self.assertEqual(0, ff._templatePatches['RES'][0][1])
def testParseMultiresiduePatch(self):
"""Test parsing a <Patch> tag that affects two residues."""
xml = """
<ForceField>
<AtomTypes>
<Type name="A type" class="A class" element="O" mass="15.99943"/>
<Type name="B type" class="B class" element="H" mass="1.007947"/>
</AtomTypes>
<Patches>
<Patch name="Test" residues="2">
<AddAtom name="1:A" type="A type"/>
<ChangeAtom name="2:B" type="B type"/>
<AddBond atomName1="1:A" atomName2="2:B"/>
<ApplyToResidue name="1:RESA"/>
<ApplyToResidue name="2:RESB"/>
</Patch>
</Patches>
</ForceField>"""
ff = ForceField(StringIO(xml))
self.assertEqual(1, len(ff._patches))
patch = ff._patches['Test']
self.assertEqual(2, len(patch.addedAtoms))
self.assertEqual(1, len(patch.addedAtoms[0]))
self.assertEqual(0, len(patch.addedAtoms[1]))
self.assertEqual(2, len(patch.changedAtoms))
self.assertEqual(0, len(patch.changedAtoms[0]))
self.assertEqual(1, len(patch.changedAtoms[1]))
self.assertEqual(1, len(patch.addedBonds))
self.assertEqual(2, len(ff._templatePatches))
self.assertEqual(1, len(ff._templatePatches['RESA']))
self.assertEqual(1, len(ff._templatePatches['RESB']))
self.assertEqual('A', patch.addedAtoms[0][0].name)
self.assertEqual('A type', patch.addedAtoms[0][0].type)
self.assertEqual('B', patch.changedAtoms[1][0].name)
self.assertEqual('B type', patch.changedAtoms[1][0].type)
self.assertEqual('A', patch.addedBonds[0][0].name)
self.assertEqual(0, patch.addedBonds[0][0].residue)
self.assertEqual('B', patch.addedBonds[0][1].name)
self.assertEqual(1, patch.addedBonds[0][1].residue)
self.assertEqual('Test', ff._templatePatches['RESA'][0][0])
self.assertEqual(0, ff._templatePatches['RESA'][0][1])
self.assertEqual('Test', ff._templatePatches['RESB'][0][0])
self.assertEqual(1, ff._templatePatches['RESB'][0][1])
def testApplyPatch(self):
"""Test applying a patch to a template."""
xml = """
<ForceField>
<AtomTypes>
<Type name="A type" class="A class" element="O" mass="15.99943"/>
<Type name="B type" class="B class" element="H" mass="1.007947"/>
<Type name="C type" class="C class" element="H" mass="1.007947"/>
<Type name="D type" class="D class" element="C" mass="12.010000"/>
</AtomTypes>
<Residues>
<Residue name="RES">
<Atom name="A" type="A type"/>
<Atom name="B" type="B type"/>
<Atom name="C" type="C type"/>
<Bond atomName1="A" atomName2="B"/>
<Bond atomName1="B" atomName2="C"/>
<ExternalBond atomName="C"/>
<VirtualSite type="average2" siteName="C" atomName1="B" atomName2="C" weight1="0.6" weight2="0.4"/>
</Residue>
</Residues>
<Patches>
<Patch name="Test">
<AddAtom name="D" type="D type"/>
<ChangeAtom name="B" type="A type"/>
<RemoveAtom name="A"/>
<AddBond atomName1="B" atomName2="D"/>
<RemoveBond atomName1="A" atomName2="B"/>
<AddExternalBond atomName="D"/>
<RemoveExternalBond atomName="C"/>
<ApplyToResidue name="RES"/>
</Patch>
</Patches>
</ForceField>"""
ff = ForceField(StringIO(xml))
self.assertEqual(1, len(ff._patches))
patch = ff._patches['Test']
template = ff._templates['RES']
newTemplates = patch.createPatchedTemplates([template])
self.assertEqual(1, len(newTemplates))
t = newTemplates[0]
self.assertEqual(3, len(t.atoms))
self.assertTrue(any(a.name == 'B' and a.type == 'A type' for a in t.atoms))
self.assertTrue(any(a.name == 'C' and a.type == 'C type' for a in t.atoms))
self.assertTrue(any(a.name == 'D' and a.type == 'D type' for a in t.atoms))
indexMap = dict([(a.name, i) for i, a in enumerate(t.atoms)])
self.assertEqual(2, len(t.bonds))
self.assertTrue((indexMap['B'], indexMap['C']) in t.bonds)
self.assertTrue((indexMap['B'], indexMap['D']) in t.bonds)
self.assertEqual(1, len(t.externalBonds))
self.assertTrue(indexMap['D'] in t.externalBonds)
self.assertEqual(1, len(t.virtualSites))
v = t.virtualSites[0]
self.assertEqual('average2', v.type)
self.assertEqual(0.6, v.weights[0])
self.assertEqual(0.4, v.weights[1])
self.assertEqual(indexMap['C'], v.index)
self.assertEqual(indexMap['B'], v.atoms[0])
self.assertEqual(indexMap['C'], v.atoms[1])
def testAlaAlaAla(self):
"""Test constructing a System that involves two patches."""
xml = """
<ForceField>
<AtomTypes>
<Type name="N" class="N" element="N" mass="14.00672"/>
<Type name="H" class="H" element="H" mass="1.007947"/>
<Type name="CT" class="CT" element="C" mass="12.01078"/>
<Type name="H1" class="H1" element="H" mass="1.007947"/>
<Type name="HC" class="HC" element="H" mass="1.007947"/>
<Type name="C" class="C" element="C" mass="12.01078"/>
<Type name="O" class="O" element="O" mass="15.99943"/>
<Type name="O2" class="O2" element="O" mass="15.99943"/>
<Type name="N3" class="N3" element="N" mass="14.00672"/>
</AtomTypes>
<Residues>
<Residue name="ALA">
<Atom name="N" type="N"/>
<Atom name="H" type="H"/>
<Atom name="CA" type="CT"/>
<Atom name="HA" type="H1"/>
<Atom name="CB" type="CT"/>
<Atom name="HB1" type="HC"/>
<Atom name="HB2" type="HC"/>
<Atom name="HB3" type="HC"/>
<Atom name="C" type="C"/>
<Atom name="O" type="O"/>
<Bond from="0" to="1"/>
<Bond from="0" to="2"/>
<Bond from="2" to="3"/>
<Bond from="2" to="4"/>
<Bond from="2" to="8"/>
<Bond from="4" to="5"/>
<Bond from="4" to="6"/>
<Bond from="4" to="7"/>
<Bond from="8" to="9"/>
<ExternalBond from="0"/>
<ExternalBond from="8"/>
<AllowPatch name="CTER"/>
<AllowPatch name="NTER"/>
</Residue>
</Residues>
<Patches>
<Patch name="CTER">
<AddAtom name="OXT" type="O2"/>
<ChangeAtom name="O" type="O2"/>
<AddBond atomName1="C" atomName2="OXT"/>
<RemoveExternalBond atomName="C"/>
</Patch>
<Patch name="NTER">
<RemoveAtom name="H"/>
<AddAtom name="H1" type="H"/>
<AddAtom name="H2" type="H"/>
<AddAtom name="H3" type="H"/>
<ChangeAtom name="N" type="N3"/>
<RemoveBond atomName1="N" atomName2="H"/>
<AddBond atomName1="N" atomName2="H1"/>
<AddBond atomName1="N" atomName2="H2"/>
<AddBond atomName1="N" atomName2="H3"/>
<RemoveExternalBond atomName="N"/>
</Patch>
</Patches>
<NonbondedForce coulomb14scale="0.833333" lj14scale="0.5">
<Atom type="N" charge="-0.4157" sigma="0.324999852378" epsilon="0.71128"/>
<Atom type="H" charge="0.2719" sigma="0.106907846177" epsilon="0.0656888"/>
<Atom type="CT" charge="0.0337" sigma="0.339966950842" epsilon="0.4577296"/>
<Atom type="H1" charge="0.0823" sigma="0.247135304412" epsilon="0.0656888"/>
<Atom type="HC" charge="0.0603" sigma="0.264953278775" epsilon="0.0656888"/>
<Atom type="C" charge="0.5973" sigma="0.339966950842" epsilon="0.359824"/>
<Atom type="O" charge="-0.5679" sigma="0.295992190115" epsilon="0.87864"/>
<Atom type="O2" charge="-0.8055" sigma="0.295992190115" epsilon="0.87864"/>
<Atom type="N3" charge="0.1414" sigma="0.324999852378" epsilon="0.71128"/>
</NonbondedForce>
</ForceField>"""
ff = ForceField(StringIO(xml))
pdb = PDBFile(os.path.join('systems', 'ala_ala_ala.pdb'))
system = ff.createSystem(pdb.topology)
nb = system.getForce(0)
expectedCharges = [0.1414, 0.2719, 0.2719, 0.2719, 0.0337, 0.0823, 0.0337, 0.0603, 0.0603, 0.0603, 0.5973, -0.5679,
-0.4157, 0.2719, 0.0337, 0.0823, 0.0337, 0.0603, 0.0603, 0.0603, 0.5973, -0.5679,
0.5973, -0.8055, -0.8055, -0.4157, 0.2719, 0.0337, 0.0823, 0.0337, 0.0603, 0.0603, 0.0603]
for i in range(system.getNumParticles()):
self.assertEqual(expectedCharges[i], nb.getParticleParameters(i)[0].value_in_unit(elementary_charge))
def testDisulfidePatch(self):
pdb = PDBFile(os.path.join('systems', 'bpti.pdb'))
ff = ForceField('amber99sb.xml')
system1 = ff.createSystem(pdb.topology)
# Override the CYX template so it will no longer match.
xml = """
<ForceField>
<Residues>
<Residue name="CYX" override="1">
</Residue>
</Residues>
</ForceField>"""
ff.loadFile(StringIO(xml))
try:
ff.createSystem(pdb.topology)
failed = False
except:
failed = True
self.assertTrue(failed)
# Now add a patch for matching disulfides.
xml = """
<ForceField>
<Patches>
<Patch name="Disulfide" residues="2">
<RemoveAtom name="1:HG"/>
<RemoveAtom name="2:HG"/>
<ChangeAtom name="1:CA" type="83"/>
<ChangeAtom name="2:CA" type="83"/>
<ChangeAtom name="1:HA" type="84"/>
<ChangeAtom name="2:HA" type="84"/>
<ChangeAtom name="1:CB" type="85"/>
<ChangeAtom name="2:CB" type="85"/>
<ChangeAtom name="1:HB2" type="86"/>
<ChangeAtom name="2:HB2" type="86"/>
<ChangeAtom name="1:HB3" type="86"/>
<ChangeAtom name="2:HB3" type="86"/>
<ChangeAtom name="1:SG" type="87"/>
<ChangeAtom name="2:SG" type="87"/>
<AddBond atomName1="1:SG" atomName2="2:SG"/>
<ApplyToResidue name="1:CYS"/>
<ApplyToResidue name="2:CYS"/>
</Patch>
</Patches>
</ForceField>"""
ff.loadFile(StringIO(xml))
system2 = ff.createSystem(pdb.topology)
self.assertEqual(XmlSerializer.serialize(system1), XmlSerializer.serialize(system2))
if __name__ == '__main__':
unittest.main()
ATOM 1 N ARG A 1 4.481 11.293 0.204
ATOM 2 CA ARG A 1 5.137 10.135 0.877
ATOM 3 C ARG A 1 5.356 9.056 -0.221
ATOM 4 O ARG A 1 4.487 8.823 -1.054
ATOM 5 CB ARG A 1 4.154 9.632 2.008
ATOM 6 CG ARG A 1 4.819 8.703 2.982
ATOM 7 CD ARG A 1 6.020 9.239 3.802
ATOM 8 NE ARG A 1 5.551 10.357 4.670
ATOM 9 CZ ARG A 1 6.185 10.965 5.621
ATOM 10 NH1 ARG A 1 7.387 10.630 5.911
ATOM 11 NH2 ARG A 1 5.512 11.845 6.297
ATOM 12 HA ARG A 1 6.074 10.533 1.267
ATOM 13 2HB ARG A 1 3.800 10.536 2.503
ATOM 14 HB3 ARG A 1 3.288 9.097 1.619
ATOM 15 2HG ARG A 1 4.077 8.489 3.751
ATOM 16 HG3 ARG A 1 5.138 7.850 2.383
ATOM 17 2HD ARG A 1 6.551 8.496 4.398
ATOM 18 HD3 ARG A 1 6.853 9.562 3.178
ATOM 19 HE ARG A 1 4.567 10.585 4.661
ATOM 20 1HH1 ARG A 1 7.769 10.908 6.804
ATOM 21 2HH1 ARG A 1 7.864 9.934 5.357
ATOM 22 1HH2 ARG A 1 4.549 12.019 6.046
ATOM 23 2HH2 ARG A 1 5.917 12.521 6.929
ATOM 24 H1 ARG A 1 3.475 11.214 0.162
ATOM 25 H2 ARG A 1 4.746 12.104 0.745
ATOM 26 H3 ARG A 1 4.816 11.478 -0.731
ATOM 27 N PRO A 2 6.483 8.417 -0.280
ATOM 28 CA PRO A 2 6.740 7.345 -1.220
ATOM 29 C PRO A 2 5.696 6.227 -1.193
ATOM 30 O PRO A 2 5.267 5.881 -0.115
ATOM 31 CB PRO A 2 8.139 6.854 -0.953
ATOM 32 CG PRO A 2 8.744 8.003 -0.281
ATOM 33 CD PRO A 2 7.675 8.665 0.535
ATOM 34 HA PRO A 2 6.709 7.735 -2.237
ATOM 35 2HB PRO A 2 8.085 6.029 -0.243
ATOM 36 HB3 PRO A 2 8.553 6.512 -1.902
ATOM 37 2HG PRO A 2 9.514 7.623 0.391
ATOM 38 HG3 PRO A 2 9.236 8.583 -1.062
ATOM 39 2HD PRO A 2 7.499 8.214 1.512
ATOM 40 HD3 PRO A 2 7.854 9.740 0.545
ATOM 41 N ASP A 3 5.540 5.671 -2.406
ATOM 42 CA ASP A 3 4.355 4.870 -2.726
ATOM 43 C ASP A 3 4.328 3.566 -1.945
ATOM 44 O ASP A 3 3.258 3.076 -1.661
ATOM 45 CB ASP A 3 4.477 4.534 -4.251
ATOM 46 CG ASP A 3 3.374 3.594 -4.902
ATOM 47 OD1 ASP A 3 2.182 3.935 -4.925
ATOM 48 OD2 ASP A 3 3.772 2.698 -5.716
ATOM 49 H ASP A 3 6.080 6.109 -3.138
ATOM 50 HA ASP A 3 3.447 5.357 -2.373
ATOM 51 2HB ASP A 3 4.514 5.458 -4.828
ATOM 52 HB3 ASP A 3 5.449 4.065 -4.403
ATOM 53 N PHE A 4 5.503 3.010 -1.562
ATOM 54 CA PHE A 4 5.732 1.868 -0.668
ATOM 55 C PHE A 4 5.581 2.197 0.775
ATOM 56 O PHE A 4 5.259 1.343 1.581
ATOM 57 CB PHE A 4 7.024 1.139 -0.960
ATOM 58 CG PHE A 4 8.248 2.039 -0.997
ATOM 59 CD1 PHE A 4 8.881 2.357 0.193
ATOM 60 CD2 PHE A 4 8.697 2.501 -2.208
ATOM 61 CE1 PHE A 4 10.030 3.230 0.154
ATOM 62 CE2 PHE A 4 9.920 3.265 -2.235
ATOM 63 CZ PHE A 4 10.511 3.709 -1.056
ATOM 64 H PHE A 4 6.284 3.533 -1.931
ATOM 65 HA PHE A 4 4.960 1.129 -0.880
ATOM 66 2HB PHE A 4 7.205 0.284 -0.309
ATOM 67 HB3 PHE A 4 6.798 0.792 -1.968
ATOM 68 HD1 PHE A 4 8.345 2.171 1.112
ATOM 69 HD2 PHE A 4 8.114 2.361 -3.106
ATOM 70 HE1 PHE A 4 10.504 3.653 1.027
ATOM 71 HE2 PHE A 4 10.343 3.666 -3.144
ATOM 72 HZ PHE A 4 11.358 4.375 -1.122
ATOM 73 N CYS A 5 5.653 3.456 1.170
ATOM 74 CA CYS A 5 5.395 3.903 2.491
ATOM 75 C CYS A 5 3.895 4.221 2.647
ATOM 76 O CYS A 5 3.457 4.281 3.830
ATOM 77 CB CYS A 5 6.278 5.096 2.648
ATOM 78 SG CYS A 5 7.989 4.948 2.335
ATOM 79 H CYS A 5 5.843 4.156 0.467
ATOM 80 HA CYS A 5 5.686 3.121 3.193
ATOM 81 2HB CYS A 5 5.930 6.055 2.266
ATOM 82 HB3 CYS A 5 6.196 5.233 3.726
ATOM 83 N LEU A 6 3.043 4.407 1.573
ATOM 84 CA LEU A 6 1.634 4.834 1.750
ATOM 85 C LEU A 6 0.674 3.656 1.891
ATOM 86 O LEU A 6 -0.530 3.825 1.989
ATOM 87 CB LEU A 6 1.407 5.795 0.588
ATOM 88 CG LEU A 6 0.016 6.561 0.500
ATOM 89 CD1 LEU A 6 -0.326 7.401 1.727
ATOM 90 CD2 LEU A 6 -0.137 7.376 -0.792
ATOM 91 H LEU A 6 3.514 4.340 0.682
ATOM 92 HA LEU A 6 1.520 5.491 2.612
ATOM 93 2HB LEU A 6 2.171 6.572 0.614
ATOM 94 HB3 LEU A 6 1.553 5.267 -0.355
ATOM 95 HG LEU A 6 -0.749 5.786 0.442
ATOM 96 1HD1 LEU A 6 0.528 8.029 1.982
ATOM 97 2HD1 LEU A 6 -1.240 7.991 1.671
ATOM 98 3HD1 LEU A 6 -0.474 6.704 2.553
ATOM 99 1HD2 LEU A 6 -1.194 7.619 -0.900
ATOM 100 2HD2 LEU A 6 0.445 8.278 -0.598
ATOM 101 3HD2 LEU A 6 0.197 6.715 -1.591
ATOM 102 N GLU A 7 1.168 2.445 1.926
ATOM 103 CA GLU A 7 0.330 1.215 1.642
ATOM 104 C GLU A 7 0.014 0.441 2.945
ATOM 105 O GLU A 7 0.707 0.600 3.925
ATOM 106 CB GLU A 7 1.018 0.404 0.587
ATOM 107 CG GLU A 7 2.236 -0.293 1.147
ATOM 108 CD GLU A 7 3.039 -1.118 0.146
ATOM 109 OE1 GLU A 7 3.059 -0.802 -1.058
ATOM 110 OE2 GLU A 7 3.537 -2.227 0.572
ATOM 111 H GLU A 7 2.176 2.482 1.972
ATOM 112 HA GLU A 7 -0.665 1.402 1.238
ATOM 113 2HB GLU A 7 0.365 -0.293 0.060
ATOM 114 HB3 GLU A 7 1.397 1.107 -0.154
ATOM 115 2HG GLU A 7 2.837 0.457 1.662
ATOM 116 HG3 GLU A 7 1.907 -0.923 1.974
ATOM 117 N PRO A 8 -1.103 -0.415 3.020
ATOM 118 CA PRO A 8 -1.381 -1.343 4.103
ATOM 119 C PRO A 8 -0.271 -2.418 4.275
ATOM 120 O PRO A 8 0.415 -2.841 3.323
ATOM 121 CB PRO A 8 -2.700 -1.908 3.748
ATOM 122 CG PRO A 8 -2.888 -1.727 2.224
ATOM 123 CD PRO A 8 -2.022 -0.525 1.858
ATOM 124 HA PRO A 8 -1.505 -0.796 5.038
ATOM 125 2HB PRO A 8 -2.657 -2.963 4.017
ATOM 126 HB3 PRO A 8 -3.448 -1.313 4.271
ATOM 127 2HG PRO A 8 -2.508 -2.611 1.712
ATOM 128 HG3 PRO A 8 -3.958 -1.528 2.153
ATOM 129 2HD PRO A 8 -1.521 -0.824 0.937
ATOM 130 HD3 PRO A 8 -2.534 0.437 1.879
ATOM 131 N PRO A 9 -0.040 -2.911 5.500
ATOM 132 CA PRO A 9 0.785 -4.139 5.864
ATOM 133 C PRO A 9 0.103 -5.504 5.481
ATOM 134 O PRO A 9 -0.988 -5.534 5.004
ATOM 135 CB PRO A 9 1.124 -3.990 7.336
ATOM 136 CG PRO A 9 -0.128 -3.245 7.915
ATOM 137 CD PRO A 9 -0.450 -2.295 6.736
ATOM 138 HA PRO A 9 1.766 -4.173 5.391
ATOM 139 2HB PRO A 9 1.311 -5.011 7.668
ATOM 140 HB3 PRO A 9 2.012 -3.394 7.546
ATOM 141 2HG PRO A 9 -1.024 -3.849 8.058
ATOM 142 HG3 PRO A 9 0.057 -2.585 8.763
ATOM 143 2HD PRO A 9 -1.519 -2.082 6.697
ATOM 144 HD3 PRO A 9 0.121 -1.374 6.854
ATOM 145 N TYR A 10 0.784 -6.614 5.626
ATOM 146 CA TYR A 10 0.338 -7.965 5.239
ATOM 147 C TYR A 10 1.070 -9.115 5.979
ATOM 148 O TYR A 10 2.288 -9.343 5.819
ATOM 149 CB TYR A 10 0.711 -8.120 3.779
ATOM 150 CG TYR A 10 0.399 -9.379 2.974
ATOM 151 CD1 TYR A 10 -0.906 -9.617 2.468
ATOM 152 CD2 TYR A 10 1.441 -10.305 2.744
ATOM 153 CE1 TYR A 10 -1.171 -10.795 1.817
ATOM 154 CE2 TYR A 10 1.171 -11.534 2.135
ATOM 155 CZ TYR A 10 -0.144 -11.787 1.672
ATOM 156 OH TYR A 10 -0.509 -12.988 1.248
ATOM 157 H TYR A 10 1.712 -6.457 5.993
ATOM 158 HA TYR A 10 -0.731 -8.039 5.436
ATOM 159 2HB TYR A 10 0.295 -7.247 3.276
ATOM 160 HB3 TYR A 10 1.796 -8.015 3.750
ATOM 161 HD1 TYR A 10 -1.717 -8.917 2.600
ATOM 162 HD2 TYR A 10 2.401 -10.213 3.231
ATOM 163 HE1 TYR A 10 -2.192 -10.947 1.499
ATOM 164 HE2 TYR A 10 1.867 -12.359 2.086
ATOM 165 HH TYR A 10 0.246 -13.495 0.940
ATOM 166 N THR A 11 0.448 -9.759 6.976
ATOM 167 CA THR A 11 1.073 -10.832 7.801
ATOM 168 C THR A 11 1.509 -12.075 6.926
ATOM 169 O THR A 11 2.482 -12.721 7.340
ATOM 170 CB THR A 11 0.108 -11.338 8.905
ATOM 171 OG1 THR A 11 -0.132 -10.234 9.629
ATOM 172 CG2 THR A 11 0.644 -12.429 9.866
ATOM 173 H THR A 11 -0.517 -9.559 7.195
ATOM 174 HA THR A 11 1.906 -10.449 8.391
ATOM 175 HB THR A 11 -0.838 -11.730 8.532
ATOM 176 HG1 THR A 11 0.614 -10.069 10.210
ATOM 177 1HG2 THR A 11 -0.268 -12.872 10.265
ATOM 178 2HG2 THR A 11 1.179 -13.130 9.225
ATOM 179 3HG2 THR A 11 1.335 -12.016 10.601
ATOM 180 N GLY A 12 0.896 -12.411 5.764
ATOM 181 CA GLY A 12 1.165 -13.584 4.948
ATOM 182 C GLY A 12 0.753 -15.016 5.427
ATOM 183 O GLY A 12 0.506 -15.143 6.609
ATOM 184 H GLY A 12 0.073 -11.936 5.422
ATOM 185 2HA GLY A 12 0.646 -13.419 4.004
ATOM 186 HA3 GLY A 12 2.228 -13.572 4.710
ATOM 187 N PRO A 13 0.761 -16.086 4.597
ATOM 188 CA PRO A 13 0.230 -17.400 5.025
ATOM 189 C PRO A 13 0.929 -18.198 6.082
ATOM 190 O PRO A 13 0.387 -19.195 6.636
ATOM 191 CB PRO A 13 0.121 -18.179 3.750
ATOM 192 CG PRO A 13 -0.021 -17.112 2.638
ATOM 193 CD PRO A 13 0.964 -15.994 3.146
ATOM 194 HA PRO A 13 -0.781 -17.292 5.417
ATOM 195 2HB PRO A 13 0.962 -18.839 3.538
ATOM 196 HB3 PRO A 13 -0.776 -18.798 3.784
ATOM 197 2HG PRO A 13 0.393 -17.595 1.753
ATOM 198 HG3 PRO A 13 -1.042 -16.752 2.510
ATOM 199 2HD PRO A 13 2.003 -16.178 2.874
ATOM 200 HD3 PRO A 13 0.539 -15.063 2.770
ATOM 201 N CYS A 14 2.190 -17.769 6.354
ATOM 202 CA CYS A 14 3.079 -18.621 7.107
ATOM 203 C CYS A 14 2.758 -18.607 8.657
ATOM 204 O CYS A 14 2.081 -17.691 9.077
ATOM 205 CB CYS A 14 4.547 -18.426 6.685
ATOM 206 SG CYS A 14 4.794 -18.857 4.907
ATOM 207 H CYS A 14 2.596 -16.879 6.102
ATOM 208 HA CYS A 14 2.904 -19.637 6.753
ATOM 209 2HB CYS A 14 4.881 -17.391 6.752
ATOM 210 HB3 CYS A 14 5.095 -19.084 7.361
ATOM 211 N LYS A 15 3.040 -19.689 9.488
ATOM 212 CA LYS A 15 2.582 -19.937 10.866
ATOM 213 C LYS A 15 3.168 -19.140 12.066
ATOM 214 O LYS A 15 2.488 -19.101 13.088
ATOM 215 CB LYS A 15 2.487 -21.458 11.142
ATOM 216 CG LYS A 15 3.842 -22.211 11.339
ATOM 217 CD LYS A 15 3.681 -23.752 11.416
ATOM 218 CE LYS A 15 5.070 -24.360 11.277
ATOM 219 NZ LYS A 15 4.972 -25.819 11.056
ATOM 220 H LYS A 15 3.526 -20.427 8.998
ATOM 221 HA LYS A 15 1.581 -19.519 10.755
ATOM 222 2HB LYS A 15 1.813 -21.575 11.991
ATOM 223 HB3 LYS A 15 2.038 -21.894 10.250
ATOM 224 2HG LYS A 15 4.476 -21.920 10.502
ATOM 225 HG3 LYS A 15 4.265 -21.803 12.257
ATOM 226 2HD LYS A 15 3.258 -23.842 12.417
ATOM 227 HD3 LYS A 15 3.027 -24.160 10.645
ATOM 228 2HE LYS A 15 5.572 -23.854 10.452
ATOM 229 HE3 LYS A 15 5.650 -24.058 12.149
ATOM 230 1HZ LYS A 15 5.891 -26.196 10.873
ATOM 231 2HZ LYS A 15 4.525 -26.294 11.827
ATOM 232 3HZ LYS A 15 4.457 -26.071 10.224
ATOM 233 N ALA A 16 4.183 -18.308 11.893
ATOM 234 CA ALA A 16 4.792 -17.419 12.911
ATOM 235 C ALA A 16 4.012 -16.102 13.171
ATOM 236 O ALA A 16 3.077 -15.829 12.483
ATOM 237 CB ALA A 16 6.245 -17.369 12.505
ATOM 238 H ALA A 16 4.660 -18.353 11.004
ATOM 239 HA ALA A 16 4.710 -17.902 13.884
ATOM 240 1HB ALA A 16 6.370 -16.590 11.752
ATOM 241 2HB ALA A 16 6.905 -17.116 13.335
ATOM 242 3HB ALA A 16 6.666 -18.338 12.238
ATOM 243 N ARG A 17 4.434 -15.382 14.265
ATOM 244 CA ARG A 17 3.794 -14.113 14.750
ATOM 245 C ARG A 17 4.885 -13.022 14.910
ATOM 246 O ARG A 17 4.694 -12.039 15.591
ATOM 247 CB ARG A 17 3.032 -14.451 16.064
ATOM 248 CG ARG A 17 2.110 -15.629 16.028
ATOM 249 CD ARG A 17 0.974 -15.737 14.993
ATOM 250 NE ARG A 17 -0.087 -14.742 15.265
ATOM 251 CZ ARG A 17 -0.609 -14.001 14.338
ATOM 252 NH1 ARG A 17 -0.400 -14.031 13.046
ATOM 253 NH2 ARG A 17 -1.470 -13.060 14.730
ATOM 254 H ARG A 17 5.184 -15.819 14.783
ATOM 255 HA ARG A 17 3.075 -13.764 14.008
ATOM 256 2HB ARG A 17 3.773 -14.712 16.819
ATOM 257 HB3 ARG A 17 2.505 -13.607 16.510
ATOM 258 2HG ARG A 17 2.638 -16.548 15.773
ATOM 259 HG3 ARG A 17 1.644 -15.713 17.010
ATOM 260 2HD ARG A 17 1.282 -15.703 13.948
ATOM 261 HD3 ARG A 17 0.632 -16.762 15.140
ATOM 262 HE ARG A 17 -0.440 -14.534 16.189
ATOM 263 1HH1 ARG A 17 0.204 -14.718 12.617
ATOM 264 2HH1 ARG A 17 -0.922 -13.340 12.528
ATOM 265 1HH2 ARG A 17 -1.774 -13.060 15.693
ATOM 266 2HH2 ARG A 17 -1.935 -12.511 14.020
ATOM 267 N ILE A 18 6.007 -13.111 14.253
ATOM 268 CA ILE A 18 7.191 -12.307 14.496
ATOM 269 C ILE A 18 6.939 -10.792 14.150
ATOM 270 O ILE A 18 6.517 -10.435 13.050
ATOM 271 CB ILE A 18 8.320 -12.892 13.575
ATOM 272 CG1 ILE A 18 8.597 -14.396 13.719
ATOM 273 CG2 ILE A 18 9.761 -12.226 13.877
ATOM 274 CD1 ILE A 18 9.360 -15.080 12.562
ATOM 275 H ILE A 18 5.960 -13.877 13.597
ATOM 276 HA ILE A 18 7.447 -12.347 15.555
ATOM 277 HB ILE A 18 8.151 -12.525 12.563
ATOM 278 2HG1 ILE A 18 9.068 -14.693 14.656
ATOM 279 HG13 ILE A 18 7.615 -14.870 13.695
ATOM 280 1HG2 ILE A 18 9.725 -11.186 13.551
ATOM 281 2HG2 ILE A 18 9.934 -12.186 14.953
ATOM 282 3HG2 ILE A 18 10.590 -12.686 13.340
ATOM 283 1HD1 ILE A 18 9.299 -16.161 12.690
ATOM 284 2HD1 ILE A 18 8.711 -15.061 11.686
ATOM 285 3HD1 ILE A 18 10.313 -14.694 12.202
ATOM 286 N ILE A 19 7.306 -9.925 15.155
ATOM 287 CA ILE A 19 7.143 -8.504 14.984
ATOM 288 C ILE A 19 8.120 -8.017 13.910
ATOM 289 O ILE A 19 9.341 -8.009 14.103
ATOM 290 CB ILE A 19 7.343 -7.858 16.368
ATOM 291 CG1 ILE A 19 6.338 -8.250 17.406
ATOM 292 CG2 ILE A 19 7.524 -6.343 16.256
ATOM 293 CD1 ILE A 19 4.922 -7.651 17.391
ATOM 294 H ILE A 19 7.787 -10.221 15.992
ATOM 295 HA ILE A 19 6.128 -8.366 14.613
ATOM 296 HB ILE A 19 8.343 -8.126 16.706
ATOM 297 2HG1 ILE A 19 6.183 -9.329 17.377
ATOM 298 HG13 ILE A 19 6.680 -8.112 18.432
ATOM 299 1HG2 ILE A 19 7.722 -5.928 17.244
ATOM 300 2HG2 ILE A 19 8.325 -6.086 15.563
ATOM 301 3HG2 ILE A 19 6.592 -5.894 15.911
ATOM 302 1HD1 ILE A 19 4.265 -8.098 18.137
ATOM 303 2HD1 ILE A 19 5.067 -6.573 17.459
ATOM 304 3HD1 ILE A 19 4.405 -7.799 16.444
ATOM 305 N ARG A 20 7.484 -7.559 12.847
ATOM 306 CA ARG A 20 8.006 -6.620 11.818
ATOM 307 C ARG A 20 7.381 -5.195 12.051
ATOM 308 O ARG A 20 6.469 -4.925 12.841
ATOM 309 CB ARG A 20 7.768 -7.155 10.394
ATOM 310 CG ARG A 20 8.471 -8.394 9.998
ATOM 311 CD ARG A 20 9.928 -8.192 10.186
ATOM 312 NE ARG A 20 10.648 -9.365 9.593
ATOM 313 CZ ARG A 20 11.302 -9.440 8.479
ATOM 314 NH1 ARG A 20 11.310 -8.543 7.656
ATOM 315 NH2 ARG A 20 12.074 -10.411 8.398
ATOM 316 H ARG A 20 6.488 -7.631 12.997
ATOM 317 HA ARG A 20 9.080 -6.560 11.990
ATOM 318 2HB ARG A 20 6.695 -7.300 10.265
ATOM 319 HB3 ARG A 20 8.047 -6.370 9.692
ATOM 320 2HG ARG A 20 8.135 -9.295 10.511
ATOM 321 HG3 ARG A 20 8.230 -8.607 8.957
ATOM 322 2HD ARG A 20 10.179 -7.264 9.672
ATOM 323 HD3 ARG A 20 10.270 -8.122 11.218
ATOM 324 HE ARG A 20 10.583 -10.257 10.062
ATOM 325 1HH1 ARG A 20 11.948 -8.654 6.881
ATOM 326 2HH1 ARG A 20 10.434 -8.064 7.501
ATOM 327 1HH2 ARG A 20 12.664 -10.491 7.582
ATOM 328 2HH2 ARG A 20 12.076 -11.064 9.168
ATOM 329 N TYR A 21 8.057 -4.274 11.316
ATOM 330 CA TYR A 21 7.877 -2.855 11.290
ATOM 331 C TYR A 21 7.370 -2.405 9.910
ATOM 332 O TYR A 21 7.715 -3.011 8.928
ATOM 333 CB TYR A 21 9.189 -2.077 11.595
ATOM 334 CG TYR A 21 9.588 -2.396 13.044
ATOM 335 CD1 TYR A 21 10.289 -3.582 13.268
ATOM 336 CD2 TYR A 21 9.131 -1.658 14.140
ATOM 337 CE1 TYR A 21 10.703 -3.986 14.591
ATOM 338 CE2 TYR A 21 9.479 -2.044 15.471
ATOM 339 CZ TYR A 21 10.212 -3.279 15.719
ATOM 340 OH TYR A 21 10.433 -3.726 16.978
ATOM 341 H TYR A 21 8.824 -4.517 10.706
ATOM 342 HA TYR A 21 7.137 -2.522 12.018
ATOM 343 2HB TYR A 21 9.930 -2.319 10.833
ATOM 344 HB3 TYR A 21 9.054 -0.996 11.570
ATOM 345 HD1 TYR A 21 10.711 -4.133 12.441
ATOM 346 HD2 TYR A 21 8.452 -0.822 14.065
ATOM 347 HE1 TYR A 21 11.369 -4.836 14.619
ATOM 348 HE2 TYR A 21 9.419 -1.347 16.293
ATOM 349 HH TYR A 21 10.182 -3.094 17.655
ATOM 350 N PHE A 22 6.477 -1.373 9.838
ATOM 351 CA PHE A 22 5.979 -0.736 8.661
ATOM 352 C PHE A 22 5.656 0.773 8.907
ATOM 353 O PHE A 22 5.717 1.236 10.042
ATOM 354 CB PHE A 22 4.736 -1.449 8.149
ATOM 355 CG PHE A 22 3.435 -1.094 8.844
ATOM 356 CD1 PHE A 22 2.423 -0.309 8.345
ATOM 357 CD2 PHE A 22 3.242 -1.546 10.167
ATOM 358 CE1 PHE A 22 1.219 -0.036 9.014
ATOM 359 CE2 PHE A 22 2.066 -1.269 10.895
ATOM 360 CZ PHE A 22 1.071 -0.451 10.318
ATOM 361 H PHE A 22 6.145 -0.982 10.708
ATOM 362 HA PHE A 22 6.686 -0.643 7.838
ATOM 363 2HB PHE A 22 4.466 -1.293 7.104
ATOM 364 HB3 PHE A 22 4.966 -2.509 8.251
ATOM 365 HD1 PHE A 22 2.560 0.133 7.368
ATOM 366 HD2 PHE A 22 3.985 -2.177 10.632
ATOM 367 HE1 PHE A 22 0.420 0.549 8.580
ATOM 368 HE2 PHE A 22 1.933 -1.708 11.873
ATOM 369 HZ PHE A 22 0.142 -0.244 10.829
ATOM 370 N TYR A 23 5.442 1.527 7.791
ATOM 371 CA TYR A 23 4.972 2.928 7.941
ATOM 372 C TYR A 23 3.452 3.055 7.889
ATOM 373 O TYR A 23 2.869 2.997 6.803
ATOM 374 CB TYR A 23 5.729 3.807 6.860
ATOM 375 CG TYR A 23 5.726 5.334 7.253
ATOM 376 CD1 TYR A 23 4.739 6.105 6.715
ATOM 377 CD2 TYR A 23 6.682 5.875 8.052
ATOM 378 CE1 TYR A 23 4.763 7.476 6.969
ATOM 379 CE2 TYR A 23 6.656 7.212 8.473
ATOM 380 CZ TYR A 23 5.633 8.031 7.885
ATOM 381 OH TYR A 23 5.600 9.331 8.298
ATOM 382 H TYR A 23 5.566 1.202 6.843
ATOM 383 HA TYR A 23 5.207 3.388 8.900
ATOM 384 2HB TYR A 23 6.777 3.513 6.820
ATOM 385 HB3 TYR A 23 5.193 3.709 5.916
ATOM 386 HD1 TYR A 23 4.053 5.744 5.963
ATOM 387 HD2 TYR A 23 7.506 5.271 8.403
ATOM 388 HE1 TYR A 23 4.097 8.209 6.539
ATOM 389 HE2 TYR A 23 7.450 7.527 9.133
ATOM 390 HH TYR A 23 6.417 9.577 8.737
ATOM 391 N ASN A 24 2.828 3.289 9.070
ATOM 392 CA ASN A 24 1.390 3.611 9.146
ATOM 393 C ASN A 24 1.145 5.035 8.696
ATOM 394 O ASN A 24 1.263 5.995 9.458
ATOM 395 CB ASN A 24 0.811 3.319 10.533
ATOM 396 CG ASN A 24 -0.680 3.743 10.614
ATOM 397 OD1 ASN A 24 -1.355 3.814 9.536
ATOM 398 ND2 ASN A 24 -1.133 4.049 11.793
ATOM 399 H ASN A 24 3.459 3.346 9.856
ATOM 400 HA ASN A 24 0.808 3.014 8.444
ATOM 401 2HB ASN A 24 0.868 2.247 10.721
ATOM 402 HB3 ASN A 24 1.391 3.841 11.295
ATOM 403 1HD2 ASN A 24 -2.114 4.252 11.924
ATOM 404 2HD2 ASN A 24 -0.547 3.660 12.517
ATOM 405 N ALA A 25 0.760 5.106 7.429
ATOM 406 CA ALA A 25 0.700 6.390 6.695
ATOM 407 C ALA A 25 -0.451 7.206 7.424
ATOM 408 O ALA A 25 -0.343 8.402 7.648
ATOM 409 CB ALA A 25 0.392 6.017 5.190
ATOM 410 H ALA A 25 0.742 4.243 6.906
ATOM 411 HA ALA A 25 1.662 6.901 6.665
ATOM 412 1HB ALA A 25 -0.470 5.364 5.051
ATOM 413 2HB ALA A 25 0.280 6.895 4.554
ATOM 414 3HB ALA A 25 1.279 5.475 4.861
ATOM 415 N LYS A 26 -1.536 6.511 7.843
ATOM 416 CA LYS A 26 -2.664 7.139 8.539
ATOM 417 C LYS A 26 -2.289 7.873 9.837
ATOM 418 O LYS A 26 -2.947 8.845 10.153
ATOM 419 CB LYS A 26 -3.810 6.050 8.872
ATOM 420 CG LYS A 26 -5.180 6.725 8.689
ATOM 421 CD LYS A 26 -6.349 5.849 9.045
ATOM 422 CE LYS A 26 -7.693 6.571 8.721
ATOM 423 NZ LYS A 26 -8.784 5.744 9.268
ATOM 424 H LYS A 26 -1.759 5.707 7.274
ATOM 425 HA LYS A 26 -3.066 7.791 7.764
ATOM 426 2HB LYS A 26 -3.735 5.225 8.163
ATOM 427 HB3 LYS A 26 -3.800 5.581 9.856
ATOM 428 2HG LYS A 26 -5.150 7.634 9.289
ATOM 429 HG3 LYS A 26 -5.213 6.939 7.620
ATOM 430 2HD LYS A 26 -6.165 4.906 8.530
ATOM 431 HD3 LYS A 26 -6.306 5.644 10.114
ATOM 432 2HE LYS A 26 -7.623 7.585 9.116
ATOM 433 HE3 LYS A 26 -7.645 6.687 7.638
ATOM 434 1HZ LYS A 26 -9.668 6.158 9.013
ATOM 435 2HZ LYS A 26 -8.755 4.780 8.968
ATOM 436 3HZ LYS A 26 -8.785 5.714 10.278
ATOM 437 N ALA A 27 -1.203 7.405 10.559
ATOM 438 CA ALA A 27 -0.686 8.124 11.724
ATOM 439 C ALA A 27 0.677 8.788 11.413
ATOM 440 O ALA A 27 1.226 9.450 12.333
ATOM 441 CB ALA A 27 -0.726 7.280 12.952
ATOM 442 H ALA A 27 -0.731 6.562 10.262
ATOM 443 HA ALA A 27 -1.415 8.910 11.919
ATOM 444 1HB ALA A 27 -0.595 7.987 13.772
ATOM 445 2HB ALA A 27 -1.711 6.845 13.123
ATOM 446 3HB ALA A 27 0.072 6.546 12.845
ATOM 447 N GLY A 28 1.248 8.681 10.218
ATOM 448 CA GLY A 28 2.403 9.458 9.885
ATOM 449 C GLY A 28 3.641 9.062 10.729
ATOM 450 O GLY A 28 4.407 9.936 11.218
ATOM 451 H GLY A 28 0.863 8.117 9.474
ATOM 452 2HA GLY A 28 2.698 9.222 8.862
ATOM 453 HA3 GLY A 28 2.226 10.500 10.151
ATOM 454 N LEU A 29 3.714 7.754 11.018
ATOM 455 CA LEU A 29 4.794 7.134 11.896
ATOM 456 C LEU A 29 5.105 5.684 11.533
ATOM 457 O LEU A 29 4.175 5.026 11.037
ATOM 458 CB LEU A 29 4.314 7.073 13.362
ATOM 459 CG LEU A 29 5.444 7.414 14.411
ATOM 460 CD1 LEU A 29 5.713 8.941 14.534
ATOM 461 CD2 LEU A 29 4.977 6.812 15.750
ATOM 462 H LEU A 29 2.939 7.177 10.723
ATOM 463 HA LEU A 29 5.695 7.744 11.845
ATOM 464 2HB LEU A 29 3.644 7.928 13.446
ATOM 465 HB3 LEU A 29 3.885 6.105 13.623
ATOM 466 HG LEU A 29 6.330 6.899 14.038
ATOM 467 1HD1 LEU A 29 4.838 9.512 14.846
ATOM 468 2HD1 LEU A 29 6.418 9.013 15.362
ATOM 469 3HD1 LEU A 29 6.104 9.386 13.619
ATOM 470 1HD2 LEU A 29 5.715 6.953 16.540
ATOM 471 2HD2 LEU A 29 4.049 7.273 16.089
ATOM 472 3HD2 LEU A 29 4.812 5.749 15.573
ATOM 473 N CYS A 30 6.299 5.155 11.773
ATOM 474 CA CYS A 30 6.628 3.768 11.766
ATOM 475 C CYS A 30 6.129 3.109 13.108
ATOM 476 O CYS A 30 6.237 3.592 14.230
ATOM 477 CB CYS A 30 8.205 3.616 11.774
ATOM 478 SG CYS A 30 9.044 4.016 10.229
ATOM 479 H CYS A 30 7.040 5.799 12.011
ATOM 480 HA CYS A 30 6.178 3.346 10.867
ATOM 481 2HB CYS A 30 8.551 4.267 12.577
ATOM 482 HB3 CYS A 30 8.458 2.574 11.970
ATOM 483 N GLN A 31 5.647 1.840 12.924
ATOM 484 CA GLN A 31 4.809 1.106 13.924
ATOM 485 C GLN A 31 4.962 -0.411 13.649
ATOM 486 O GLN A 31 5.518 -0.779 12.632
ATOM 487 CB GLN A 31 3.334 1.617 13.977
ATOM 488 CG GLN A 31 3.039 3.023 14.324
ATOM 489 CD GLN A 31 1.528 3.328 14.415
ATOM 490 OE1 GLN A 31 0.713 2.677 13.797
ATOM 491 NE2 GLN A 31 1.181 4.459 15.046
ATOM 492 H GLN A 31 5.671 1.475 11.982
ATOM 493 HA GLN A 31 5.211 1.188 14.934
ATOM 494 2HB GLN A 31 2.906 1.543 12.978
ATOM 495 HB3 GLN A 31 2.752 0.961 14.624
ATOM 496 2HG GLN A 31 3.509 3.268 15.276
ATOM 497 HG3 GLN A 31 3.562 3.586 13.550
ATOM 498 1HE2 GLN A 31 0.247 4.834 15.130
ATOM 499 2HE2 GLN A 31 1.961 4.885 15.525
ATOM 500 N THR A 32 4.466 -1.218 14.575
ATOM 501 CA THR A 32 4.558 -2.660 14.660
ATOM 502 C THR A 32 3.360 -3.332 13.955
ATOM 503 O THR A 32 2.204 -2.870 14.037
ATOM 504 CB THR A 32 4.760 -3.213 16.144
ATOM 505 OG1 THR A 32 3.848 -2.618 17.043
ATOM 506 CG2 THR A 32 6.172 -2.908 16.645
ATOM 507 H THR A 32 4.011 -0.903 15.420
ATOM 508 HA THR A 32 5.457 -2.945 14.113
ATOM 509 HB THR A 32 4.559 -4.284 16.182
ATOM 510 HG1 THR A 32 3.372 -3.323 17.488
ATOM 511 1HG2 THR A 32 6.365 -1.851 16.827
ATOM 512 2HG2 THR A 32 6.244 -3.338 17.644
ATOM 513 3HG2 THR A 32 6.923 -3.285 15.951
ATOM 514 N PHE A 33 3.704 -4.519 13.398
ATOM 515 CA PHE A 33 2.741 -5.558 13.036
ATOM 516 C PHE A 33 3.316 -6.967 13.044
ATOM 517 O PHE A 33 4.513 -7.185 12.939
ATOM 518 CB PHE A 33 2.029 -5.210 11.699
ATOM 519 CG PHE A 33 2.826 -5.715 10.519
ATOM 520 CD1 PHE A 33 2.321 -6.793 9.740
ATOM 521 CD2 PHE A 33 4.015 -5.088 10.095
ATOM 522 CE1 PHE A 33 3.093 -7.286 8.608
ATOM 523 CE2 PHE A 33 4.645 -5.509 8.900
ATOM 524 CZ PHE A 33 4.206 -6.608 8.178
ATOM 525 H PHE A 33 4.662 -4.695 13.131
ATOM 526 HA PHE A 33 1.985 -5.629 13.818
ATOM 527 2HB PHE A 33 1.082 -5.729 11.847
ATOM 528 HB3 PHE A 33 1.749 -4.165 11.565
ATOM 529 HD1 PHE A 33 1.524 -7.355 10.205
ATOM 530 HD2 PHE A 33 4.280 -4.231 10.697
ATOM 531 HE1 PHE A 33 2.721 -8.132 8.050
ATOM 532 HE2 PHE A 33 5.503 -4.950 8.558
ATOM 533 HZ PHE A 33 4.799 -6.963 7.348
ATOM 534 N VAL A 34 2.452 -7.989 13.168
ATOM 535 CA VAL A 34 2.912 -9.385 13.166
ATOM 536 C VAL A 34 2.894 -10.062 11.815
ATOM 537 O VAL A 34 2.043 -9.804 10.991
ATOM 538 CB VAL A 34 2.134 -10.252 14.125
ATOM 539 CG1 VAL A 34 2.112 -9.683 15.584
ATOM 540 CG2 VAL A 34 0.588 -10.294 13.716
ATOM 541 H VAL A 34 1.525 -7.851 13.546
ATOM 542 HA VAL A 34 3.951 -9.341 13.493
ATOM 543 HB VAL A 34 2.590 -11.242 14.160
ATOM 544 1HG1 VAL A 34 3.133 -9.744 15.961
ATOM 545 2HG1 VAL A 34 1.983 -8.603 15.514
ATOM 546 3HG1 VAL A 34 1.473 -10.263 16.248
ATOM 547 1HG2 VAL A 34 0.190 -9.281 13.644
ATOM 548 2HG2 VAL A 34 0.469 -10.701 12.712
ATOM 549 3HG2 VAL A 34 0.106 -10.985 14.407
ATOM 550 N TYR A 35 3.814 -10.982 11.645
ATOM 551 CA TYR A 35 4.160 -11.476 10.334
ATOM 552 C TYR A 35 4.542 -12.988 10.394
ATOM 553 O TYR A 35 5.414 -13.426 11.118
ATOM 554 CB TYR A 35 5.203 -10.543 9.874
ATOM 555 CG TYR A 35 6.004 -10.946 8.613
ATOM 556 CD1 TYR A 35 5.348 -10.903 7.365
ATOM 557 CD2 TYR A 35 7.332 -11.351 8.673
ATOM 558 CE1 TYR A 35 6.062 -11.323 6.208
ATOM 559 CE2 TYR A 35 8.123 -11.576 7.524
ATOM 560 CZ TYR A 35 7.464 -11.535 6.278
ATOM 561 OH TYR A 35 8.148 -11.824 5.176
ATOM 562 H TYR A 35 4.601 -10.930 12.276
ATOM 563 HA TYR A 35 3.297 -11.395 9.673
ATOM 564 2HB TYR A 35 4.731 -9.574 9.714
ATOM 565 HB3 TYR A 35 5.981 -10.439 10.630
ATOM 566 HD1 TYR A 35 4.319 -10.579 7.297
ATOM 567 HD2 TYR A 35 7.900 -11.256 9.587
ATOM 568 HE1 TYR A 35 5.595 -11.384 5.236
ATOM 569 HE2 TYR A 35 9.187 -11.761 7.543
ATOM 570 HH TYR A 35 9.094 -11.880 5.329
ATOM 571 N GLY A 36 4.106 -13.725 9.407
ATOM 572 CA GLY A 36 4.181 -15.187 9.310
ATOM 573 C GLY A 36 5.541 -15.769 8.895
ATOM 574 O GLY A 36 5.752 -16.993 9.080
ATOM 575 H GLY A 36 3.488 -13.296 8.733
ATOM 576 2HA GLY A 36 4.030 -15.572 10.318
ATOM 577 HA3 GLY A 36 3.287 -15.514 8.779
ATOM 578 N GLY A 37 6.518 -14.952 8.545
ATOM 579 CA GLY A 37 7.863 -15.378 8.107
ATOM 580 C GLY A 37 7.965 -15.804 6.669
ATOM 581 O GLY A 37 8.796 -16.604 6.374
ATOM 582 H GLY A 37 6.260 -13.985 8.684
ATOM 583 2HA GLY A 37 8.656 -14.644 8.247
ATOM 584 HA3 GLY A 37 8.115 -16.335 8.566
ATOM 585 N CYS A 38 7.161 -15.319 5.791
ATOM 586 CA CYS A 38 7.170 -15.482 4.311
ATOM 587 C CYS A 38 6.413 -14.398 3.594
ATOM 588 O CYS A 38 5.578 -13.722 4.185
ATOM 589 CB CYS A 38 6.754 -16.815 3.824
ATOM 590 SG CYS A 38 4.937 -17.145 3.840
ATOM 591 H CYS A 38 6.538 -14.628 6.184
ATOM 592 HA CYS A 38 8.215 -15.445 4.001
ATOM 593 2HB CYS A 38 7.174 -16.971 2.830
ATOM 594 HB3 CYS A 38 7.211 -17.580 4.452
ATOM 595 N ARG A 39 6.772 -14.282 2.293
ATOM 596 CA ARG A 39 6.097 -13.526 1.228
ATOM 597 C ARG A 39 5.858 -12.029 1.522
ATOM 598 O ARG A 39 4.698 -11.587 1.496
ATOM 599 CB ARG A 39 4.867 -14.303 0.628
ATOM 600 CG ARG A 39 5.207 -15.756 0.229
ATOM 601 CD ARG A 39 4.004 -16.561 -0.314
ATOM 602 NE ARG A 39 4.562 -17.905 -0.627
ATOM 603 CZ ARG A 39 3.936 -19.017 -0.980
ATOM 604 NH1 ARG A 39 2.638 -19.088 -1.273
ATOM 605 NH2 ARG A 39 4.621 -20.147 -0.945
ATOM 606 H ARG A 39 7.524 -14.812 1.875
ATOM 607 HA ARG A 39 6.866 -13.477 0.457
ATOM 608 2HB ARG A 39 4.111 -14.544 1.375
ATOM 609 HB3 ARG A 39 4.241 -13.755 -0.076
ATOM 610 2HG ARG A 39 6.053 -15.727 -0.458
ATOM 611 HG3 ARG A 39 5.573 -16.373 1.050
ATOM 612 2HD ARG A 39 3.292 -16.747 0.490
ATOM 613 HD3 ARG A 39 3.615 -16.098 -1.221
ATOM 614 HE ARG A 39 5.566 -18.012 -0.659
ATOM 615 1HH1 ARG A 39 2.080 -18.250 -1.185
ATOM 616 2HH1 ARG A 39 2.079 -19.870 -1.582
ATOM 617 1HH2 ARG A 39 5.594 -20.041 -0.696
ATOM 618 2HH2 ARG A 39 4.182 -21.041 -0.781
ATOM 619 N ALA A 40 6.854 -11.319 1.959
ATOM 620 CA ALA A 40 6.717 -9.865 2.308
ATOM 621 C ALA A 40 5.884 -9.145 1.334
ATOM 622 O ALA A 40 6.154 -9.243 0.108
ATOM 623 CB ALA A 40 8.136 -9.265 2.420
ATOM 624 H ALA A 40 7.762 -11.748 2.068
ATOM 625 HA ALA A 40 6.337 -9.868 3.330
ATOM 626 1HB ALA A 40 8.817 -9.431 1.585
ATOM 627 2HB ALA A 40 8.098 -8.262 2.847
ATOM 628 3HB ALA A 40 8.665 -9.799 3.209
ATOM 629 N LYS A 41 5.081 -8.146 1.750
ATOM 630 CA LYS A 41 4.813 -6.980 0.893
ATOM 631 C LYS A 41 5.903 -5.937 1.023
ATOM 632 O LYS A 41 6.776 -5.994 1.909
ATOM 633 CB LYS A 41 3.350 -6.565 1.046
ATOM 634 CG LYS A 41 2.435 -7.643 0.544
ATOM 635 CD LYS A 41 1.060 -7.188 0.196
ATOM 636 CE LYS A 41 0.165 -8.134 -0.614
ATOM 637 NZ LYS A 41 -0.895 -7.367 -1.214
ATOM 638 H LYS A 41 4.946 -8.044 2.746
ATOM 639 HA LYS A 41 4.756 -7.366 -0.124
ATOM 640 2HB LYS A 41 3.106 -6.504 2.106
ATOM 641 HB3 LYS A 41 3.236 -5.633 0.491
ATOM 642 2HG LYS A 41 2.831 -7.969 -0.418
ATOM 643 HG3 LYS A 41 2.471 -8.562 1.130
ATOM 644 2HD LYS A 41 0.561 -6.954 1.136
ATOM 645 HD3 LYS A 41 1.045 -6.187 -0.235
ATOM 646 2HE LYS A 41 0.715 -8.613 -1.424
ATOM 647 HE3 LYS A 41 -0.192 -8.938 0.030
ATOM 648 1HZ LYS A 41 -1.548 -6.955 -0.563
ATOM 649 2HZ LYS A 41 -0.547 -6.734 -1.919
ATOM 650 3HZ LYS A 41 -1.437 -7.994 -1.792
ATOM 651 N ARG A 42 5.880 -4.879 0.157
ATOM 652 CA ARG A 42 6.897 -3.891 0.135
ATOM 653 C ARG A 42 7.090 -3.129 1.442
ATOM 654 O ARG A 42 8.237 -2.801 1.847
ATOM 655 CB ARG A 42 6.572 -2.752 -0.892
ATOM 656 CG ARG A 42 6.433 -3.218 -2.267
ATOM 657 CD ARG A 42 6.403 -2.043 -3.308
ATOM 658 NE ARG A 42 5.289 -1.052 -3.048
ATOM 659 CZ ARG A 42 4.668 -0.234 -3.928
ATOM 660 NH1 ARG A 42 4.993 -0.206 -5.192
ATOM 661 NH2 ARG A 42 3.718 0.513 -3.507
ATOM 662 H ARG A 42 5.203 -4.900 -0.593
ATOM 663 HA ARG A 42 7.833 -4.353 -0.179
ATOM 664 2HB ARG A 42 5.650 -2.257 -0.587
ATOM 665 HB3 ARG A 42 7.335 -1.977 -0.828
ATOM 666 2HG ARG A 42 7.265 -3.863 -2.550
ATOM 667 HG3 ARG A 42 5.523 -3.793 -2.434
ATOM 668 2HD ARG A 42 7.384 -1.576 -3.395
ATOM 669 HD3 ARG A 42 6.162 -2.367 -4.321
ATOM 670 HE ARG A 42 4.750 -1.203 -2.207
ATOM 671 1HH1 ARG A 42 4.811 0.637 -5.717
ATOM 672 2HH1 ARG A 42 5.740 -0.845 -5.423
ATOM 673 1HH2 ARG A 42 3.275 0.317 -2.620
ATOM 674 2HH2 ARG A 42 3.204 1.143 -4.106
ATOM 675 N ASN A 43 5.973 -2.758 2.160
ATOM 676 CA ASN A 43 5.975 -2.121 3.481
ATOM 677 C ASN A 43 6.208 -3.110 4.663
ATOM 678 O ASN A 43 5.292 -3.470 5.397
ATOM 679 CB ASN A 43 4.754 -1.247 3.721
ATOM 680 CG ASN A 43 4.921 -0.094 4.704
ATOM 681 OD1 ASN A 43 5.872 0.050 5.351
ATOM 682 ND2 ASN A 43 3.863 0.719 4.723
ATOM 683 H ASN A 43 5.079 -2.804 1.692
ATOM 684 HA ASN A 43 6.848 -1.475 3.384
ATOM 685 2HB ASN A 43 4.375 -0.760 2.822
ATOM 686 HB3 ASN A 43 3.973 -1.902 4.107
ATOM 687 1HD2 ASN A 43 3.076 0.604 4.100
ATOM 688 2HD2 ASN A 43 3.782 1.482 5.380
ATOM 689 N ASN A 44 7.442 -3.651 4.757
ATOM 690 CA ASN A 44 7.760 -4.726 5.781
ATOM 691 C ASN A 44 9.267 -4.609 5.992
ATOM 692 O ASN A 44 9.984 -4.694 5.047
ATOM 693 CB ASN A 44 7.427 -6.179 5.245
ATOM 694 CG ASN A 44 7.725 -7.283 6.219
ATOM 695 OD1 ASN A 44 8.653 -7.241 7.033
ATOM 696 ND2 ASN A 44 7.057 -8.391 6.034
ATOM 697 H ASN A 44 8.172 -3.465 4.085
ATOM 698 HA ASN A 44 7.221 -4.689 6.728
ATOM 699 2HB ASN A 44 6.369 -6.170 4.983
ATOM 700 HB3 ASN A 44 7.979 -6.327 4.316
ATOM 701 1HD2 ASN A 44 7.209 -9.098 6.739
ATOM 702 2HD2 ASN A 44 6.285 -8.431 5.383
ATOM 703 N PHE A 45 9.665 -4.190 7.168
ATOM 704 CA PHE A 45 10.990 -3.890 7.611
ATOM 705 C PHE A 45 11.339 -4.643 8.918
ATOM 706 O PHE A 45 10.511 -4.965 9.791
ATOM 707 CB PHE A 45 11.184 -2.337 7.715
ATOM 708 CG PHE A 45 10.654 -1.448 6.615
ATOM 709 CD1 PHE A 45 9.429 -0.733 6.916
ATOM 710 CD2 PHE A 45 11.263 -1.279 5.372
ATOM 711 CE1 PHE A 45 8.888 0.180 5.973
ATOM 712 CE2 PHE A 45 10.652 -0.483 4.424
ATOM 713 CZ PHE A 45 9.478 0.282 4.699
ATOM 714 H PHE A 45 8.932 -4.150 7.862
ATOM 715 HA PHE A 45 11.579 -4.294 6.788
ATOM 716 2HB PHE A 45 10.703 -2.065 8.654
ATOM 717 HB3 PHE A 45 12.241 -2.211 7.953
ATOM 718 HD1 PHE A 45 8.935 -0.819 7.872
ATOM 719 HD2 PHE A 45 12.151 -1.858 5.166
ATOM 720 HE1 PHE A 45 8.066 0.817 6.265
ATOM 721 HE2 PHE A 45 11.056 -0.566 3.426
ATOM 722 HZ PHE A 45 9.044 0.854 3.892
ATOM 723 N LYS A 46 12.628 -4.866 9.013
ATOM 724 CA LYS A 46 13.280 -5.405 10.215
ATOM 725 C LYS A 46 13.335 -4.493 11.399
ATOM 726 O LYS A 46 13.449 -4.951 12.547
ATOM 727 CB LYS A 46 14.730 -5.783 9.887
ATOM 728 CG LYS A 46 15.689 -4.616 9.542
ATOM 729 CD LYS A 46 17.095 -4.998 9.051
ATOM 730 CE LYS A 46 17.022 -5.652 7.759
ATOM 731 NZ LYS A 46 18.359 -5.783 7.094
ATOM 732 H LYS A 46 13.134 -4.664 8.163
ATOM 733 HA LYS A 46 12.825 -6.346 10.525
ATOM 734 2HB LYS A 46 15.174 -6.393 10.674
ATOM 735 HB3 LYS A 46 14.632 -6.503 9.074
ATOM 736 2HG LYS A 46 15.232 -3.816 8.960
ATOM 737 HG3 LYS A 46 16.032 -4.230 10.501
ATOM 738 2HD LYS A 46 17.759 -4.134 9.073
ATOM 739 HD3 LYS A 46 17.515 -5.773 9.691
ATOM 740 2HE LYS A 46 16.450 -6.575 7.857
ATOM 741 HE3 LYS A 46 16.469 -4.994 7.089
ATOM 742 1HZ LYS A 46 18.289 -6.042 6.120
ATOM 743 2HZ LYS A 46 18.959 -4.979 7.205
ATOM 744 3HZ LYS A 46 18.922 -6.541 7.454
ATOM 745 N SER A 47 13.134 -3.218 11.175
ATOM 746 CA SER A 47 13.349 -2.293 12.285
ATOM 747 C SER A 47 12.694 -0.923 11.947
ATOM 748 O SER A 47 12.532 -0.518 10.757
ATOM 749 CB SER A 47 14.866 -2.123 12.645
ATOM 750 OG SER A 47 15.642 -1.718 11.559
ATOM 751 H SER A 47 13.233 -2.909 10.219
ATOM 752 HA SER A 47 12.882 -2.674 13.193
ATOM 753 2HB SER A 47 14.933 -1.428 13.482
ATOM 754 HB3 SER A 47 15.294 -3.094 12.895
ATOM 755 HG SER A 47 16.556 -1.903 11.785
ATOM 756 N ALA A 48 12.375 -0.176 12.984
ATOM 757 CA ALA A 48 11.836 1.205 12.902
ATOM 758 C ALA A 48 13.040 2.166 12.619
ATOM 759 O ALA A 48 12.846 3.238 12.092
ATOM 760 CB ALA A 48 11.053 1.480 14.189
ATOM 761 H ALA A 48 12.322 -0.637 13.881
ATOM 762 HA ALA A 48 11.138 1.234 12.066
ATOM 763 1HB ALA A 48 10.159 0.856 14.158
ATOM 764 2HB ALA A 48 11.798 1.116 14.896
ATOM 765 3HB ALA A 48 10.853 2.539 14.347
ATOM 766 N GLU A 49 14.269 1.696 12.738
ATOM 767 CA GLU A 49 15.549 2.323 12.263
ATOM 768 C GLU A 49 15.719 2.179 10.791
ATOM 769 O GLU A 49 15.858 3.202 10.072
ATOM 770 CB GLU A 49 16.632 1.725 13.112
ATOM 771 CG GLU A 49 18.128 1.981 12.727
ATOM 772 CD GLU A 49 19.158 1.239 13.644
ATOM 773 OE1 GLU A 49 20.058 0.577 13.143
ATOM 774 OE2 GLU A 49 18.948 1.246 14.886
ATOM 775 H GLU A 49 14.400 0.821 13.225
ATOM 776 HA GLU A 49 15.454 3.387 12.483
ATOM 777 2HB GLU A 49 16.366 2.199 14.056
ATOM 778 HB3 GLU A 49 16.450 0.653 13.037
ATOM 779 2HG GLU A 49 18.136 1.596 11.708
ATOM 780 HG3 GLU A 49 18.356 3.047 12.715
ATOM 781 N ASP A 50 15.449 1.003 10.228
ATOM 782 CA ASP A 50 15.332 0.878 8.725
ATOM 783 C ASP A 50 14.041 1.488 8.071
ATOM 784 O ASP A 50 14.120 2.223 7.069
ATOM 785 CB ASP A 50 15.633 -0.573 8.282
ATOM 786 CG ASP A 50 15.895 -0.626 6.774
ATOM 787 OD1 ASP A 50 16.848 0.049 6.344
ATOM 788 OD2 ASP A 50 15.221 -1.371 5.994
ATOM 789 H ASP A 50 15.155 0.218 10.791
ATOM 790 HA ASP A 50 16.161 1.422 8.272
ATOM 791 2HB ASP A 50 16.524 -0.852 8.844
ATOM 792 HB3 ASP A 50 14.821 -1.269 8.494
ATOM 793 N CYS A 51 12.845 1.354 8.656
ATOM 794 CA CYS A 51 11.618 2.060 8.308
ATOM 795 C CYS A 51 11.758 3.594 8.285
ATOM 796 O CYS A 51 11.353 4.228 7.311
ATOM 797 CB CYS A 51 10.506 1.644 9.228
ATOM 798 SG CYS A 51 8.832 2.388 9.059
ATOM 799 H CYS A 51 12.798 0.918 9.566
ATOM 800 HA CYS A 51 11.237 1.680 7.360
ATOM 801 2HB CYS A 51 10.393 0.561 9.184
ATOM 802 HB3 CYS A 51 10.791 1.961 10.232
ATOM 803 N MET A 52 12.378 4.204 9.299
ATOM 804 CA MET A 52 12.708 5.603 9.261
ATOM 805 C MET A 52 13.856 6.049 8.296
ATOM 806 O MET A 52 13.895 7.083 7.678
ATOM 807 CB MET A 52 12.970 6.087 10.726
ATOM 808 CG MET A 52 11.722 6.125 11.691
ATOM 809 SD MET A 52 12.141 6.646 13.366
ATOM 810 CE MET A 52 13.123 5.328 14.092
ATOM 811 H MET A 52 12.758 3.665 10.064
ATOM 812 HA MET A 52 11.855 6.179 8.904
ATOM 813 2HB MET A 52 13.736 5.442 11.157
ATOM 814 HB3 MET A 52 13.368 7.101 10.752
ATOM 815 2HG MET A 52 10.975 6.787 11.253
ATOM 816 HG3 MET A 52 11.234 5.156 11.792
ATOM 817 1HE MET A 52 12.556 4.411 14.253
ATOM 818 2HE MET A 52 14.012 5.183 13.478
ATOM 819 3HE MET A 52 13.450 5.730 15.051
ATOM 820 N ARG A 53 14.821 5.165 8.029
ATOM 821 CA ARG A 53 15.884 5.306 7.040
ATOM 822 C ARG A 53 15.300 5.136 5.579
ATOM 823 O ARG A 53 15.792 5.696 4.634
ATOM 824 CB ARG A 53 17.046 4.333 7.370
ATOM 825 CG ARG A 53 18.257 4.651 6.467
ATOM 826 CD ARG A 53 19.511 3.977 6.716
ATOM 827 NE ARG A 53 19.279 2.554 6.875
ATOM 828 CZ ARG A 53 19.602 1.776 7.898
ATOM 829 NH1 ARG A 53 20.396 2.076 8.869
ATOM 830 NH2 ARG A 53 19.123 0.539 7.954
ATOM 831 H ARG A 53 14.949 4.313 8.556
ATOM 832 HA ARG A 53 16.289 6.317 7.093
ATOM 833 2HB ARG A 53 17.345 4.503 8.404
ATOM 834 HB3 ARG A 53 16.712 3.301 7.265
ATOM 835 2HG ARG A 53 17.986 4.585 5.413
ATOM 836 HG3 ARG A 53 18.426 5.725 6.542
ATOM 837 2HD ARG A 53 20.226 4.008 5.894
ATOM 838 HD3 ARG A 53 19.785 4.278 7.727
ATOM 839 HE ARG A 53 18.788 2.067 6.138
ATOM 840 1HH1 ARG A 53 20.621 1.358 9.543
ATOM 841 2HH1 ARG A 53 21.040 2.852 8.807
ATOM 842 1HH2 ARG A 53 18.381 0.314 7.306
ATOM 843 2HH2 ARG A 53 19.382 -0.106 8.687
ATOM 844 N THR A 54 14.153 4.454 5.497
ATOM 845 CA THR A 54 13.448 4.173 4.196
ATOM 846 C THR A 54 12.283 5.120 3.871
ATOM 847 O THR A 54 12.211 5.569 2.723
ATOM 848 CB THR A 54 13.092 2.651 4.096
ATOM 849 OG1 THR A 54 14.248 1.842 4.091
ATOM 850 CG2 THR A 54 12.397 2.301 2.778
ATOM 851 H THR A 54 13.893 4.034 6.378
ATOM 852 HA THR A 54 14.193 4.293 3.409
ATOM 853 HB THR A 54 12.469 2.218 4.879
ATOM 854 HG1 THR A 54 14.440 1.874 5.031
ATOM 855 1HG2 THR A 54 12.609 1.260 2.532
ATOM 856 2HG2 THR A 54 11.329 2.516 2.740
ATOM 857 3HG2 THR A 54 12.858 2.856 1.961
ATOM 858 N CYS A 55 11.414 5.465 4.884
ATOM 859 CA CYS A 55 10.100 6.043 4.700
ATOM 860 C CYS A 55 9.920 7.406 5.386
ATOM 861 O CYS A 55 8.855 7.913 5.661
ATOM 862 CB CYS A 55 9.033 4.974 5.073
ATOM 863 SG CYS A 55 8.585 3.686 3.823
ATOM 864 H CYS A 55 11.490 4.844 5.677
ATOM 865 HA CYS A 55 10.087 6.261 3.632
ATOM 866 2HB CYS A 55 9.447 4.531 5.979
ATOM 867 HB3 CYS A 55 8.111 5.482 5.354
ATOM 868 N GLY A 56 11.088 8.105 5.711
ATOM 869 CA GLY A 56 11.076 9.290 6.702
ATOM 870 C GLY A 56 10.557 10.564 5.980
ATOM 871 O GLY A 56 10.245 11.546 6.679
ATOM 872 H GLY A 56 12.000 7.772 5.435
ATOM 873 2HA GLY A 56 10.290 9.053 7.420
ATOM 874 HA3 GLY A 56 12.083 9.460 7.083
ATOM 875 N GLY A 57 10.609 10.624 4.640
ATOM 876 CA GLY A 57 10.474 11.834 3.826
ATOM 877 C GLY A 57 9.017 12.157 3.304
ATOM 878 O GLY A 57 8.350 11.205 2.818
ATOM 879 H GLY A 57 10.889 9.765 4.190
ATOM 880 2HA GLY A 57 10.807 12.611 4.514
ATOM 881 HA3 GLY A 57 11.067 11.892 2.913
ATOM 882 N ALA A 58 8.698 13.452 3.182
ATOM 883 CA ALA A 58 7.286 13.898 2.917
ATOM 884 C ALA A 58 6.836 13.499 1.596
ATOM 885 O ALA A 58 5.636 13.446 1.333
ATOM 886 CB ALA A 58 7.261 15.464 2.893
ATOM 887 OXT ALA A 58 7.692 13.378 0.682
ATOM 888 H ALA A 58 9.320 14.138 3.585
ATOM 889 HA ALA A 58 6.658 13.407 3.660
ATOM 890 1HB ALA A 58 7.720 15.835 3.810
ATOM 891 2HB ALA A 58 7.761 15.829 1.996
ATOM 892 3HB ALA A 58 6.238 15.811 3.037
ENDMDL
CRYST1 12.009 12.338 11.510 90.00 90.00 90.00 P 1
ATOM 1 SOD SOD I 1 -5.844 1.432 3.239 1.00 0.00 ION NA
ATOM 2 CLA CLA I 2 -5.605 -3.153 1.213 1.00 0.00 ION CL
END
\ No newline at end of file
PSF
4 !NTITLE
REMARKS original generated structure x-plor psf file
REMARKS topology ../../../../../Charmm36_FF/toppar/top_all36_cgenff.rtf
REMARKS topology ../../../../../Charmm36_FF/toppar/toppar_water_ions.str
REMARKS segment ION { first NONE; last NONE; auto angles dihedrals }
2 !NATOM
1 ION 1 SOD SOD SOD 1.000000 22.9898 0
2 ION 2 CLA CLA CLA -1.000000 35.4500 0
0 !NBOND: bonds
0 !NTHETA: angles
0 !NPHI: dihedrals
0 !NIMPHI: impropers
0 !NDON: donors
0 !NACC: acceptors
0 !NNB
0 0
1 0 !NGRP
0 0 0
CRYST1 12.009 12.338 11.510 90.00 90.00 90.00 P 1 1
ATOM 1 CB MEOHM 1 -0.748 -0.015 0.024 1.00 0.00 METH C
ATOM 2 OG MEOHM 1 0.558 0.420 -0.278 1.00 0.00 METH O
ATOM 3 HG1 MEOHM 1 0.716 1.404 0.137 1.00 0.00 METH H
ATOM 4 HB1 MEOHM 1 -1.293 -0.202 -0.901 1.00 0.00 METH H
ATOM 5 HB2 MEOHM 1 -1.263 0.754 0.600 1.00 0.00 METH H
ATOM 6 HB3 MEOHM 1 -0.699 -0.934 0.609 1.00 0.00 METH H
ATOM 7 SOD SOD I 2 -5.844 1.432 3.239 1.00 0.00 ION NA
ATOM 8 CLA CLA I 3 -5.605 -3.153 1.213 1.00 0.00 ION CL
CONECT 1 2 4 5 6
CONECT 2 3
END
END
\ No newline at end of file
PSF EXT
4 !NTITLE
REMARKS original generated structure x-plor psf file
REMARKS topology ../../../../../Charmm36_FF/toppar/top_all36_cgenff.rtf
REMARKS topology ../../../../../Charmm36_FF/toppar/toppar_water_ions.str
REMARKS segment ION { first NONE; last NONE; auto angles dihedrals }
8 !NATOM
1 METH 1 MEOH CB CG331 -0.040000 12.0110 0
2 METH 1 MEOH OG OG311 -0.650000 15.9994 0
3 METH 1 MEOH HG1 HGP1 0.420000 1.0080 0
4 METH 1 MEOH HB1 HGA3 0.090000 1.0080 0
5 METH 1 MEOH HB2 HGA3 0.090000 1.0080 0
6 METH 1 MEOH HB3 HGA3 0.090000 1.0080 0
7 ION 2 SOD SOD SOD 1.000000 22.9898 0
8 ION 3 CLA CLA CLA -1.000000 35.4500 0
5 !NBOND: bonds
1 2 1 4 1 5 1 6
2 3
7 !NTHETA: angles
1 2 3 2 1 6 2 1 5
2 1 4 4 1 6 4 1 5
5 1 6
3 !NPHI: dihedrals
3 2 1 4 3 2 1 5
3 2 1 6
0 !NIMPHI: impropers
0 !NDON: donors
0 !NACC: acceptors
0 !NNB
0 0 0 0 0 0 0 0
1 0 !NGRP
0 0 0
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
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