def computeLengthsAndAngles(periodicBoxVectors): """Convert periodic box vectors to lengths and angles. Lengths are returned in nanometers and angles in radians. """ if is_quantity(periodicBoxVectors): (a, b, c) = periodicBoxVectors.value_in_unit(nanometers) else: a, b, c = periodicBoxVectors a_length = norm(a) b_length = norm(b) c_length = norm(c) alpha = math.acos(dot(b, c) / (b_length * c_length)) beta = math.acos(dot(c, a) / (c_length * a_length)) gamma = math.acos(dot(a, b) / (a_length * b_length)) return (a_length, b_length, c_length, alpha, beta, gamma)
def computeLengthsAndAngles(periodicBoxVectors): """Convert periodic box vectors to lengths and angles. Lengths are returned in nanometers and angles in radians. """ if is_quantity(periodicBoxVectors): (a, b, c) = periodicBoxVectors.value_in_unit(nanometers) else: a, b, c = periodicBoxVectors a_length = norm(a) b_length = norm(b) c_length = norm(c) alpha = math.acos(dot(b, c)/(b_length*c_length)) beta = math.acos(dot(c, a)/(c_length*a_length)) gamma = math.acos(dot(a, b)/(a_length*b_length)) return (a_length, b_length, c_length, alpha, beta, gamma)
def isHbond(d, h, a): if norm(d-a) > 0.35*nanometer: return False deltaDH = h-d deltaHA = a-h deltaDH /= norm(deltaDH) deltaHA /= norm(deltaHA) return acos(dot(deltaDH, deltaHA)) < 50*degree
def isHbond(d, h, a): if norm(d - a) > 0.35 * nanometer: return False deltaDH = h - d deltaHA = a - h deltaDH /= norm(deltaDH) deltaHA /= norm(deltaHA) return acos(dot(deltaDH, deltaHA)) < 50 * degree
def writeHeader(topology, file=sys.stdout): """Write out the header for a PDB file. Parameters: - topology (Topology) The Topology defining the molecular system being written - file (file=stdout) A file to write the file to """ print >> file, "REMARK 1 CREATED WITH OPENMM %s, %s" % ( Platform.getOpenMMVersion(), str(date.today())) vectors = topology.getPeriodicBoxVectors() if vectors is not None: (a, b, c) = vectors.value_in_unit(angstroms) a_length = norm(a) b_length = norm(b) c_length = norm(c) alpha = math.acos(dot(b, c) / (b_length * c_length)) * 180.0 / math.pi beta = math.acos(dot(c, a) / (c_length * a_length)) * 180.0 / math.pi gamma = math.acos(dot(a, b) / (a_length * b_length)) * 180.0 / math.pi print >> file, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f P 1 1 " % ( a_length, b_length, c_length, alpha, beta, gamma)
def testUnitMathModule(self): """ Tests the unit_math functions on Quantity objects """ self.assertEqual(u.sqrt(1.0*u.kilogram*u.joule), 1.0*u.kilogram*u.meter/u.second) self.assertEqual(u.sqrt(1.0*u.kilogram*u.calorie), math.sqrt(4.184)*u.kilogram*u.meter/u.second) self.assertEqual(u.sqrt(9), 3) # Test on a scalar self.assertEqual(u.sin(90*u.degrees), 1) self.assertEqual(u.sin(math.pi/2*u.radians), 1) self.assertEqual(u.sin(math.pi/2), 1) self.assertEqual(u.cos(180*u.degrees), -1) self.assertEqual(u.cos(math.pi*u.radians), -1) self.assertEqual(u.cos(math.pi), -1) self.assertAlmostEqual(u.tan(45*u.degrees), 1) self.assertAlmostEqual(u.tan(math.pi/4*u.radians), 1) self.assertAlmostEqual(u.tan(math.pi/4), 1) acos = u.acos(1.0) asin = u.asin(1.0) atan = u.atan(1.0) self.assertTrue(u.is_quantity(acos)) self.assertTrue(u.is_quantity(asin)) self.assertTrue(u.is_quantity(atan)) self.assertEqual(acos.unit, u.radians) self.assertEqual(asin.unit, u.radians) self.assertEqual(atan.unit, u.radians) self.assertEqual(acos.value_in_unit(u.degrees), 0) self.assertEqual(acos / u.radians, 0) self.assertEqual(asin.value_in_unit(u.degrees), 90) self.assertEqual(asin / u.radians, math.pi/2) self.assertAlmostEqual(atan.value_in_unit(u.degrees), 45) self.assertAlmostEqual(atan / u.radians, math.pi/4) # Check some sequence maths seq = [1, 2, 3, 4] * u.meters self.assertEqual(u.sum(seq), 10*u.meters) self.assertEqual(u.dot(seq, seq), (1+4+9+16)*u.meters**2) self.assertEqual(u.norm(seq), math.sqrt(30)*u.meters)
def _addAtomsToTopology(self, heavyAtomsOnly, omitUnknownMolecules): """Create a new Topology in which missing atoms have been added. Parameters ---------- heavyAtomsOnly : bool If True, only heavy atoms will be added to the topology. omitUnknownMolecules : bool If True, unknown molecules will be omitted from the topology. Returns ------- newTopology : simtk.openmm.app.Topology A new Topology object containing atoms from the old. newPositions : list of simtk.unit.Quantity with units compatible with nanometers Atom positions for the new Topology object. newAtoms : simtk.openmm.app.Topology.Atom New atom objects. existingAtomMap : dict Mapping from old atoms to new atoms. """ newTopology = app.Topology() newPositions = []*unit.nanometer newAtoms = [] existingAtomMap = {} addedAtomMap = {} addedOXT = [] residueCenters = [self._computeResidueCenter(res).value_in_unit(unit.nanometers) for res in self.topology.residues()]*unit.nanometers for chain in self.topology.chains(): if omitUnknownMolecules and not any(residue.name in self.templates for residue in chain.residues()): continue chainResidues = list(chain.residues()) newChain = newTopology.addChain(chain.id) for indexInChain, residue in enumerate(chain.residues()): # Insert missing residues here. if (chain.index, indexInChain) in self.missingResidues: insertHere = self.missingResidues[(chain.index, indexInChain)] endPosition = self._computeResidueCenter(residue) if indexInChain > 0: startPosition = self._computeResidueCenter(chainResidues[indexInChain-1]) loopDirection = _findUnoccupiedDirection((startPosition+endPosition)/2, residueCenters) else: outward = _findUnoccupiedDirection(endPosition, residueCenters)*unit.nanometers norm = unit.norm(outward) if norm > 0*unit.nanometer: outward *= len(insertHere)*0.5*unit.nanometer/norm startPosition = endPosition+outward loopDirection = None firstIndex = int(residue.id)-len(insertHere) self._addMissingResiduesToChain(newChain, insertHere, startPosition, endPosition, loopDirection, residue, newAtoms, newPositions, firstIndex) # Create the new residue and add existing heavy atoms. newResidue = newTopology.addResidue(residue.name, newChain, residue.id) addResiduesAfter = (residue == chainResidues[-1] and (chain.index, indexInChain+1) in self.missingResidues) for atom in residue.atoms(): if not heavyAtomsOnly or (atom.element is not None and atom.element != hydrogen): if atom.name == 'OXT' and (chain.index, indexInChain+1) in self.missingResidues: continue # Remove terminal oxygen, since we'll add more residues after this one newAtom = newTopology.addAtom(atom.name, atom.element, newResidue) existingAtomMap[atom] = newAtom newPositions.append(self.positions[atom.index]) if residue in self.missingAtoms: # Find corresponding atoms in the residue and the template. template = self.templates[residue.name] atomPositions = dict((atom.name, self.positions[atom.index]) for atom in residue.atoms()) points1 = [] points2 = [] for atom in template.topology.atoms(): if atom.name in atomPositions: points1.append(atomPositions[atom.name].value_in_unit(unit.nanometer)) points2.append(template.positions[atom.index].value_in_unit(unit.nanometer)) # Compute the optimal transform to overlay them. (translate2, rotate, translate1) = _overlayPoints(points1, points2) # Add the missing atoms. addedAtomMap[residue] = {} for atom in self.missingAtoms[residue]: newAtom = newTopology.addAtom(atom.name, atom.element, newResidue) newAtoms.append(newAtom) addedAtomMap[residue][atom] = newAtom templatePosition = template.positions[atom.index].value_in_unit(unit.nanometer) newPositions.append((mm.Vec3(*np.dot(rotate, templatePosition+translate2))+translate1)*unit.nanometer) if residue in self.missingTerminals: terminalsToAdd = self.missingTerminals[residue] else: terminalsToAdd = None # If this is the end of the chain, add any missing residues that come after it. if residue == chainResidues[-1] and (chain.index, indexInChain+1) in self.missingResidues: insertHere = self.missingResidues[(chain.index, indexInChain+1)] if len(insertHere) > 0: startPosition = self._computeResidueCenter(residue) outward = _findUnoccupiedDirection(startPosition, residueCenters)*unit.nanometers norm = unit.norm(outward) if norm > 0*unit.nanometer: outward *= len(insertHere)*0.5*unit.nanometer/norm endPosition = startPosition+outward firstIndex = int(residue.id)+1 self._addMissingResiduesToChain(newChain, insertHere, startPosition, endPosition, None, residue, newAtoms, newPositions, firstIndex) newResidue = list(newChain.residues())[-1] if newResidue.name in proteinResidues: terminalsToAdd = ['OXT'] else: terminalsToAdd = None # If a terminal OXT is missing, add it. if terminalsToAdd is not None: atomPositions = dict((atom.name, newPositions[atom.index].value_in_unit(unit.nanometer)) for atom in newResidue.atoms()) if 'OXT' in terminalsToAdd: newAtom = newTopology.addAtom('OXT', oxygen, newResidue) newAtoms.append(newAtom) addedOXT.append(newAtom) d_ca_o = atomPositions['O']-atomPositions['CA'] d_ca_c = atomPositions['C']-atomPositions['CA'] d_ca_c /= unit.sqrt(unit.dot(d_ca_c, d_ca_c)) v = d_ca_o - d_ca_c*unit.dot(d_ca_c, d_ca_o) newPositions.append((atomPositions['O']+2*v)*unit.nanometer) newTopology.setUnitCellDimensions(self.topology.getUnitCellDimensions()) newTopology.createStandardBonds() newTopology.createDisulfideBonds(newPositions) # Return the results. return (newTopology, newPositions, newAtoms, existingAtomMap)
def _addAtomsToTopology(self, heavyAtomsOnly, omitUnknownMolecules): """Create a new Topology in which missing atoms have been added.""" newTopology = app.Topology() newPositions = []*unit.nanometer newAtoms = [] existingAtomMap = {} addedAtomMap = {} addedOXT = [] for chain in self.topology.chains(): if omitUnknownMolecules and not any(residue.name in self.templates for residue in chain.residues()): continue chainResidues = list(chain.residues()) newChain = newTopology.addChain() for indexInChain, residue in enumerate(chain.residues()): # Insert missing residues here. if (chain.index, indexInChain) in self.missingResidues: insertHere = self.missingResidues[(chain.index, indexInChain)] endPosition = self._computeResidueCenter(residue) if indexInChain > 0: startPosition = self._computeResidueCenter(chainResidues[indexInChain-1]) else: outward = endPosition-self.centroid norm = unit.norm(outward) if norm > 0*unit.nanometer: outward *= len(insertHere)*0.5*unit.nanometer/norm startPosition = endPosition+outward self._addMissingResiduesToChain(newChain, insertHere, startPosition, endPosition, residue, newAtoms, newPositions) # Create the new residue and add existing heavy atoms. newResidue = newTopology.addResidue(residue.name, newChain) addResiduesAfter = (residue == chainResidues[-1] and (chain.index, indexInChain+1) in self.missingResidues) for atom in residue.atoms(): if not heavyAtomsOnly or (atom.element is not None and atom.element != hydrogen): if atom.name == 'OXT' and (chain.index, indexInChain+1) in self.missingResidues: continue # Remove terminal oxygen, since we'll add more residues after this one newAtom = newTopology.addAtom(atom.name, atom.element, newResidue) existingAtomMap[atom] = newAtom newPositions.append(self.positions[atom.index]) if residue in self.missingAtoms: # Find corresponding atoms in the residue and the template. template = self.templates[residue.name] atomPositions = dict((atom.name, self.positions[atom.index]) for atom in residue.atoms()) points1 = [] points2 = [] for atom in template.topology.atoms(): if atom.name in atomPositions: points1.append(atomPositions[atom.name].value_in_unit(unit.nanometer)) points2.append(template.positions[atom.index].value_in_unit(unit.nanometer)) # Compute the optimal transform to overlay them. (translate2, rotate, translate1) = _overlayPoints(points1, points2) # Add the missing atoms. addedAtomMap[residue] = {} for atom in self.missingAtoms[residue]: newAtom = newTopology.addAtom(atom.name, atom.element, newResidue) newAtoms.append(newAtom) addedAtomMap[residue][atom] = newAtom templatePosition = template.positions[atom.index].value_in_unit(unit.nanometer) newPositions.append((mm.Vec3(*np.dot(rotate, templatePosition+translate2))+translate1)*unit.nanometer) if residue in self.missingTerminals: terminalsToAdd = self.missingTerminals[residue] else: terminalsToAdd = None # If this is the end of the chain, add any missing residues that come after it. if residue == chainResidues[-1] and (chain.index, indexInChain+1) in self.missingResidues: insertHere = self.missingResidues[(chain.index, indexInChain+1)] if len(insertHere) > 0: startPosition = self._computeResidueCenter(residue) outward = startPosition-self.centroid norm = unit.norm(outward) if norm > 0*unit.nanometer: outward *= len(insertHere)*0.5*unit.nanometer/norm endPosition = startPosition+outward self._addMissingResiduesToChain(newChain, insertHere, startPosition, endPosition, residue, newAtoms, newPositions) newResidue = list(newChain.residues())[-1] if newResidue.name in proteinResidues: terminalsToAdd = ['OXT'] else: terminalsToAdd = None # If a terminal OXT is missing, add it. if terminalsToAdd is not None: atomPositions = dict((atom.name, newPositions[atom.index].value_in_unit(unit.nanometer)) for atom in newResidue.atoms()) if 'OXT' in terminalsToAdd: newAtom = newTopology.addAtom('OXT', oxygen, newResidue) newAtoms.append(newAtom) addedOXT.append(newAtom) d_ca_o = atomPositions['O']-atomPositions['CA'] d_ca_c = atomPositions['C']-atomPositions['CA'] d_ca_c /= unit.sqrt(unit.dot(d_ca_c, d_ca_c)) v = d_ca_o - d_ca_c*unit.dot(d_ca_c, d_ca_o) newPositions.append((atomPositions['O']+2*v)*unit.nanometer) newTopology.setUnitCellDimensions(self.topology.getUnitCellDimensions()) newTopology.createStandardBonds() newTopology.createDisulfideBonds(newPositions) # Return the results. return (newTopology, newPositions, newAtoms, existingAtomMap)
def runTest(path, systemName, platformName, eps, tolerance, precision, queue): """This function runs the test for a single system.""" print "\nsystemName =", systemName # load the system from the xml file system = loadXMLFile(path, systemName) # set Ewald error tolerance to a sufficiently small value so the requested accuracy will be achievable for f in system.getForces(): try: f.setEwaldErrorTolerance(tolerance/2) except: pass # read in the particle positions from the .pos file positions = loadPosFile(path, systemName) numParticles = len(positions) print "numParticles =", numParticles, "(from the .pos file)" integrator = mm.LangevinIntegrator(300*unit.kelvin, 1/unit.picosecond, 0.002*unit.picoseconds) print "mm.Platform.getNumPlatforms =", mm.Platform.getNumPlatforms() platform = mm.Platform.getPlatformByName(platformName) print "platform.getName() =", platform.getName() print "Building \'context\' on \'platform\'." context = mm.Context(system, integrator, platform, properties) context.setPositions(positions) state0 = context.getState(getForces=True) forces0 = state0.getForces() # make sure the size of the force vector is equal to the number of particles assert (len(forces0)==numParticles) # calculate the norm of the forces force0NormSum = 0.0*unit.kilojoules**2/unit.mole**2/unit.nanometer**2 for f in forces0: force0NormSum += unit.dot(f,f) force0Norm = unit.sqrt(force0NormSum) print "force0Norm =", force0Norm epsilon = eps*unit.nanometer step = epsilon/force0Norm print "step =", step # perturb the coordinates along the direction of forces0 and evaluate the energy context.setPositions([p-2*f*step for p,f in zip(positions, forces0)]) pe1 = context.getState(getEnergy=True).getPotentialEnergy() context.setPositions([p-f*step for p,f in zip(positions, forces0)]) pe2 = context.getState(getEnergy=True).getPotentialEnergy() context.setPositions([p+f*step for p,f in zip(positions, forces0)]) pe3 = context.getState(getEnergy=True).getPotentialEnergy() context.setPositions([p+2*f*step for p,f in zip(positions, forces0)]) pe4 = context.getState(getEnergy=True).getPotentialEnergy() # use a finite difference approximation to calculate the expected force0Norm expectedForceNorm = (-pe1+8*pe2-8*pe3+pe4)/(12*epsilon) relativeDifference = abs(expectedForceNorm-force0Norm)/force0Norm print "pe1 =", pe1 print "pe2 =", pe2 print "pe3 =", pe3 print "pe4 =", pe4 print "expectedForceNorm =", expectedForceNorm print "relativeDifference =", relativeDifference # check that the energy is within the desired range if relativeDifference > tolerance: print "*** ERROR EXCEEDS TOLERANCE ***" queue.put(False) else: queue.put(True) print "Test of", systemName, "complete." del(context)
def createRigidBodies(system, positions, bodies): """Modify a System to turn specified sets of particles into rigid bodies. For every rigid body, four particles are selected as "real" particles whose positions are integrated. Constraints are added between them to make them move as a rigid body. All other particles in the body are then turned into virtual sites whose positions are computed based on the "real" particles. Because virtual sites are massless, the mass properties of the rigid bodies will be slightly different from the corresponding sets of particles in the original system. The masses of the non-virtual particles are chosen to guarantee that the total mass and center of mass of each rigid body exactly match those of the original particles. The moment of inertia will be similar to that of the original particles, but not identical. Care is needed when using constraints, since virtual particles cannot participate in constraints. If the input system includes any constraints, this function will automatically remove ones that connect two particles in the same rigid body. But if there is a constraint beween a particle in a rigid body and another particle not in that body, it will likely lead to an exception when you try to create a context. Parameters: - system (System) the System to modify - positions (list) the positions of all particles in the system - bodies (list) each element of this list defines one rigid body. Each element should itself be a list of the indices of all particles that make up that rigid body. """ # Remove any constraints involving particles in rigid bodies. for i in range(system.getNumConstraints() - 1, -1, -1): p1, p2, distance = system.getConstraintParameters(i) if (any(p1 in body and p2 in body for body in bodies)): system.removeConstraint(i) # Loop over rigid bodies and process them. for particles in bodies: if len(particles) < 5: # All the particles will be "real" particles. realParticles = particles realParticleMasses = [system.getParticleMass(i) for i in particles] else: # Select four particles to use as the "real" particles. All others will be virtual sites. pos = [positions[i] for i in particles] mass = [system.getParticleMass(i) for i in particles] cm = unit.sum([p * m for p, m in zip(pos, mass)]) / unit.sum(mass) r = [p - cm for p in pos] avgR = unit.sqrt( unit.sum([unit.dot(x, x) for x in r]) / len(particles)) rank = sorted(range(len(particles)), key=lambda i: abs(unit.norm(r[i]) - avgR)) for p in combinations(rank, 4): # Select masses for the "real" particles. If any is negative, reject this set of particles # and keep going. matrix = np.zeros((4, 4)) for i in range(4): particleR = r[p[i]].value_in_unit(unit.nanometers) matrix[0][i] = particleR[0] matrix[1][i] = particleR[1] matrix[2][i] = particleR[2] matrix[3][i] = 1.0 rhs = np.array( [0.0, 0.0, 0.0, unit.sum(mass).value_in_unit(unit.amu)]) weights = lin.solve(matrix, rhs) if all(w > 0.0 for w in weights): # We have a good set of particles. realParticles = [particles[i] for i in p] realParticleMasses = [float(w) for w in weights] * unit.amu break # Set particle masses. for i, m in zip(realParticles, realParticleMasses): system.setParticleMass(i, m) # Add constraints between the real particles. for p1, p2 in combinations(realParticles, 2): distance = unit.norm(positions[p1] - positions[p2]) key = (min(p1, p2), max(p1, p2)) system.addConstraint(p1, p2, distance) # Select which three particles to use for defining virtual sites. bestNorm = 0 for p1, p2, p3 in combinations(realParticles, 3): d12 = (positions[p2] - positions[p1]).value_in_unit(unit.nanometer) d13 = (positions[p3] - positions[p1]).value_in_unit(unit.nanometer) crossNorm = unit.norm((d12[1] * d13[2] - d12[2] * d13[1], d12[2] * d13[0] - d12[0] * d13[2], d12[0] * d13[1] - d12[1] * d13[0])) if crossNorm > bestNorm: bestNorm = crossNorm vsiteParticles = (p1, p2, p3) # Create virtual sites. d12 = (positions[vsiteParticles[1]] - positions[vsiteParticles[0]]).value_in_unit(unit.nanometer) d13 = (positions[vsiteParticles[2]] - positions[vsiteParticles[0]]).value_in_unit(unit.nanometer) cross = mm.Vec3(d12[1] * d13[2] - d12[2] * d13[1], d12[2] * d13[0] - d12[0] * d13[2], d12[0] * d13[1] - d12[1] * d13[0]) matrix = np.zeros((3, 3)) for i in range(3): matrix[i][0] = d12[i] matrix[i][1] = d13[i] matrix[i][2] = cross[i] for i in particles: if i not in realParticles: system.setParticleMass(i, 0) rhs = np.array((positions[i] - positions[vsiteParticles[0]]).value_in_unit( unit.nanometer)) weights = lin.solve(matrix, rhs) system.setVirtualSite( i, mm.OutOfPlaneSite(vsiteParticles[0], vsiteParticles[1], vsiteParticles[2], weights[0], weights[1], weights[2]))