from simtk.openmm.app import PDBFile from simtk.openmm.app.topology import Topology ### Rename residues and heavy atoms ## Create new topology with residues renamed # Load topology pdb = PDBFile("input/6lu7.pdb") old_topology = pdb.getTopology() # Create new topology new_topology = Topology() new_topology.setPeriodicBoxVectors(old_topology.getPeriodicBoxVectors()) # Copy residues and atoms to new topology and rename residues/atoms in chain C d_old_to_new = {} for chain in old_topology.chains(): old_chain_id = chain.id new_chain = new_topology.addChain(id=old_chain_id) if chain.id != 'C': for res in chain.residues(): # Copy residues and atoms new_res = new_topology.addResidue(res.name, new_chain, id=res.id, insertionCode=res.insertionCode) for atom in res.atoms(): new_atom = new_topology.addAtom(atom.name, atom.element, new_res) d_old_to_new[atom] = new_atom
return # # TEST HARNESS # if __name__ == '__main__': # Read OpenMM System and coordinates. from simtk import openmm from simtk import unit system = openmm.XmlSerializer.deserialize(_readFileContents('explicit-system.xml')) state = openmm.XmlSerializer.deserialize(_readFileContents('explicit-state.xml')) # Read explicitly solvated PDB file. from simtk.openmm.app import PDBFile pdb_filename = 'explicit-refined.pdb' pdbfile = PDBFile(pdb_filename) topology = pdbfile.getTopology() # Create AMBER input files. prmtop_filename = 'amber.prmtop' inpcrd_filename = 'amber.crd' openmm_forcefields_to_use = ['amber99sbildn.xml', 'tip3p.xml'] amber_forcefield_to_use = '/mnt/b/projects/sciteam/jn6/GIT/amber-gnu/dat/leap/cmd/oldff/leaprc.ff99SBildn' createAmberInputFiles(topology, system, state, openmm_forcefields_to_use, amber_forcefield_to_use, prmtop_filename, inpcrd_filename, verbose=True) # Run dynamics. ntimesteps = 5000 run_pmemd_cuda(ntimesteps, verbose=True)
def addSolvent(self, forcefield, model='tip3p', boxSize=None, padding=None, positiveIon='Na+', negativeIon='Cl-', ionicStrength=0*molar): """Add solvent (both water and ions) to the model to fill a rectangular box. The algorithm works as follows: 1. Water molecules are added to fill the box. 2. Water molecules are removed if their distance to any solute atom is less than the sum of their van der Waals radii. 3. If the solute is charged, enough positive or negative ions are added to neutralize it. Each ion is added by randomly selecting a water molecule and replacing it with the ion. 4. Ion pairs are added to give the requested total ionic strength. The box size can be specified in three ways. First, you can explicitly give a box size to use. Alternatively, you can give a padding distance. The largest dimension of the solute (along the x, y, or z axis) is determined, and a cubic box of size (largest dimension)+2*padding is used. Finally, if neither a box size nor a padding distance is specified, the existing Topology's unit cell dimensions are used. Parameters: - forcefield (ForceField) the ForceField to use for determining van der Waals radii and atomic charges - model (string='tip3p') the water model to use. Supported values are 'tip3p', 'spce', 'tip4pew', and 'tip5p'. - boxSize (Vec3=None) the size of the box to fill with water - padding (distance=None) the padding distance to use - positiveIon (string='Na+') the type of positive ion to add. Allowed values are 'Cs+', 'K+', 'Li+', 'Na+', and 'Rb+' - negativeIon (string='Cl-') the type of negative ion to add. Allowed values are 'Cl-', 'Br-', 'F-', and 'I-'. Be aware that not all force fields support all ion types. - ionicString (concentration=0*molar) the total concentration of ions (both positive and negative) to add. This does not include ions that are added to neutralize the system. """ # Pick a unit cell size. if boxSize is not None: if is_quantity(boxSize): boxSize = boxSize.value_in_unit(nanometer) box = Vec3(boxSize[0], boxSize[1], boxSize[2])*nanometer elif padding is not None: maxSize = max(max((pos[i] for pos in self.positions))-min((pos[i] for pos in self.positions)) for i in range(3)) box = (maxSize+2*padding)*Vec3(1, 1, 1) else: box = self.topology.getUnitCellDimensions() if box is None: raise ValueError('Neither the box size nor padding was specified, and the Topology does not define unit cell dimensions') box = box.value_in_unit(nanometer) invBox = Vec3(1.0/box[0], 1.0/box[1], 1.0/box[2]) # Identify the ion types. posIonElements = {'Cs+':elem.cesium, 'K+':elem.potassium, 'Li+':elem.lithium, 'Na+':elem.sodium, 'Rb+':elem.rubidium} negIonElements = {'Cl-':elem.chlorine, 'Br-':elem.bromine, 'F-':elem.fluorine, 'I-':elem.iodine} if positiveIon not in posIonElements: raise ValueError('Illegal value for positive ion: %s' % positiveIon) if negativeIon not in negIonElements: raise ValueError('Illegal value for negative ion: %s' % negativeIon) positiveElement = posIonElements[positiveIon] negativeElement = negIonElements[negativeIon] # Load the pre-equilibrated water box. vdwRadiusPerSigma = 0.5612310241546864907 if model == 'tip3p': waterRadius = 0.31507524065751241*vdwRadiusPerSigma elif model == 'spce': waterRadius = 0.31657195050398818*vdwRadiusPerSigma elif model == 'tip4pew': waterRadius = 0.315365*vdwRadiusPerSigma elif model == 'tip5p': waterRadius = 0.312*vdwRadiusPerSigma else: raise ValueError('Unknown water model: %s' % model) pdb = PDBFile(os.path.join(os.path.dirname(__file__), 'data', model+'.pdb')) pdbTopology = pdb.getTopology() pdbPositions = pdb.getPositions().value_in_unit(nanometer) pdbResidues = list(pdbTopology.residues()) pdbBoxSize = pdbTopology.getUnitCellDimensions().value_in_unit(nanometer) # Have the ForceField build a System for the solute from which we can determine van der Waals radii. system = forcefield.createSystem(self.topology) nonbonded = None for i in range(system.getNumForces()): if isinstance(system.getForce(i), NonbondedForce): nonbonded = system.getForce(i) if nonbonded is None: raise ValueError('The ForceField does not specify a NonbondedForce') cutoff = [nonbonded.getParticleParameters(i)[1].value_in_unit(nanometer)*vdwRadiusPerSigma+waterRadius for i in range(system.getNumParticles())] waterCutoff = waterRadius if len(cutoff) == 0: maxCutoff = waterCutoff else: maxCutoff = max(waterCutoff, max(cutoff)) # Copy the solute over. newTopology = Topology() newTopology.setUnitCellDimensions(box) newAtoms = {} newPositions = []*nanometer for chain in self.topology.chains(): newChain = newTopology.addChain() for residue in chain.residues(): newResidue = newTopology.addResidue(residue.name, newChain) for atom in residue.atoms(): newAtom = newTopology.addAtom(atom.name, atom.element, newResidue) newAtoms[atom] = newAtom newPositions.append(deepcopy(self.positions[atom.index])) for bond in self.topology.bonds(): newTopology.addBond(newAtoms[bond[0]], newAtoms[bond[1]]) # Sort the solute atoms into cells for fast lookup. if len(self.positions) == 0: positions = [] else: positions = self.positions.value_in_unit(nanometer) cells = {} numCells = tuple((max(1, int(floor(box[i]/maxCutoff))) for i in range(3))) cellSize = tuple((box[i]/numCells[i] for i in range(3))) for i in range(len(positions)): cell = tuple((int(floor(positions[i][j]/cellSize[j]))%numCells[j] for j in range(3))) if cell in cells: cells[cell].append(i) else: cells[cell] = [i] # Create a generator that loops over atoms close to a position. def neighbors(pos): centralCell = tuple((int(floor(pos[i]/cellSize[i])) for i in range(3))) offsets = (-1, 0, 1) for i in offsets: for j in offsets: for k in offsets: cell = ((centralCell[0]+i+numCells[0])%numCells[0], (centralCell[1]+j+numCells[1])%numCells[1], (centralCell[2]+k+numCells[2])%numCells[2]) if cell in cells: for atom in cells[cell]: yield atom # Define a function to compute the distance between two points, taking periodic boundary conditions into account. def periodicDistance(pos1, pos2): delta = pos1-pos2 delta = [delta[i]-floor(delta[i]*invBox[i]+0.5)*box[i] for i in range(3)] return norm(delta) # Find the list of water molecules to add. newChain = newTopology.addChain() if len(positions) == 0: center = Vec3(0, 0, 0) else: center = [(max((pos[i] for pos in positions))+min((pos[i] for pos in positions)))/2 for i in range(3)] center = Vec3(center[0], center[1], center[2]) numBoxes = [int(ceil(box[i]/pdbBoxSize[i])) for i in range(3)] addedWaters = [] for boxx in range(numBoxes[0]): for boxy in range(numBoxes[1]): for boxz in range(numBoxes[2]): offset = Vec3(boxx*pdbBoxSize[0], boxy*pdbBoxSize[1], boxz*pdbBoxSize[2]) for residue in pdbResidues: oxygen = [atom for atom in residue.atoms() if atom.element == elem.oxygen][0] atomPos = pdbPositions[oxygen.index]+offset if not any((atomPos[i] > box[i] for i in range(3))): # This molecule is inside the box, so see how close to it is to the solute. atomPos += center-box/2 for i in neighbors(atomPos): if periodicDistance(atomPos, positions[i]) < cutoff[i]: break else: # Record this water molecule as one to add. addedWaters.append((residue.index, atomPos)) # There could be clashes between water molecules at the box edges. Find ones to remove. upperCutoff = center+box/2-Vec3(waterCutoff, waterCutoff, waterCutoff) lowerCutoff = center-box/2+Vec3(waterCutoff, waterCutoff, waterCutoff) lowerSkinPositions = [pos for index, pos in addedWaters if pos[0] < lowerCutoff[0] or pos[1] < lowerCutoff[1] or pos[2] < lowerCutoff[2]] filteredWaters = [] cells = {} for i in range(len(lowerSkinPositions)): cell = tuple((int(floor(lowerSkinPositions[i][j]/cellSize[j]))%numCells[j] for j in range(3))) if cell in cells: cells[cell].append(i) else: cells[cell] = [i] for entry in addedWaters: pos = entry[1] if pos[0] < upperCutoff[0] and pos[1] < upperCutoff[1] and pos[2] < upperCutoff[2]: filteredWaters.append(entry) else: if not any((periodicDistance(lowerSkinPositions[i], pos) < waterCutoff and norm(lowerSkinPositions[i]-pos) > waterCutoff for i in neighbors(pos))): filteredWaters.append(entry) addedWaters = filteredWaters # Add ions to neutralize the system. totalCharge = int(floor(0.5+sum((nonbonded.getParticleParameters(i)[0].value_in_unit(elementary_charge) for i in range(system.getNumParticles()))))) if abs(totalCharge) > len(addedWaters): raise Exception('Cannot neutralize the system because the charge is greater than the number of available positions for ions') def addIon(element): # Replace a water by an ion. index = random.randint(0, len(addedWaters)-1) newResidue = newTopology.addResidue(element.symbol.upper(), newChain) newTopology.addAtom(element.symbol, element, newResidue) newPositions.append(addedWaters[index][1]*nanometer) del addedWaters[index] for i in range(abs(totalCharge)): addIon(positiveElement if totalCharge < 0 else negativeElement) # Add ions based on the desired ionic strength. numIons = len(addedWaters)*ionicStrength/(55.4*molar) # Pure water is about 55.4 molar (depending on temperature) numPairs = int(floor(numIons/2+0.5)) for i in range(numPairs): addIon(positiveElement) for i in range(numPairs): addIon(negativeElement) # Add the water molecules. for index, pos in addedWaters: newResidue = newTopology.addResidue(residue.name, newChain) residue = pdbResidues[index] oxygen = [atom for atom in residue.atoms() if atom.element == elem.oxygen][0] oPos = pdbPositions[oxygen.index] molAtoms = [] for atom in residue.atoms(): molAtoms.append(newTopology.addAtom(atom.name, atom.element, newResidue)) newPositions.append((pos+pdbPositions[atom.index]-oPos)*nanometer) for atom1 in molAtoms: if atom1.element == elem.oxygen: for atom2 in molAtoms: if atom2.element == elem.hydrogen: newTopology.addBond(atom1, atom2) newTopology.setUnitCellDimensions(deepcopy(box)*nanometer) self.topology = newTopology self.positions = newPositions
print(os.environ) print(Platform.getPluginLoadFailures()) print(Platform.getDefaultPluginsDirectory()) if args.restart: arr = np.load(args.restart) simulation.context.setPositions(arr['positions'] * u.nanometers) simulation.context.setVelocities(arr['velocities'] * u.nanometers / u.picosecond) simulation.context.setPeriodicBoxVectors(*arr['box_vectors'] * u.nanometers) else: simulation.context.setPositions(pdb.positions) pbv = pdb.getTopology().getPeriodicBoxVectors() simulation.context.setPeriodicBoxVectors(*pbv) # set velocities to temperature in integrator try: temperature = integrator.getTemperature() except AttributeError: assert args.temperature > 0 temperature = args.temperature * u.kelvin print('# temperature:', temperature) simulation.context.setVelocitiesToTemperature(temperature) output = args.output if args.types: