def add_hydrogens_by_openmm(self): from simtk.openmm.app import ForceField, Modeller, PDBFile from pdbfixer import PDBFixer fixer = PDBFixer(self.name) field = ForceField('amber99sb.xml', 'tip3p.xml') fixer.findMissingResidues() fixer.findMissingAtoms() fixer.addMissingAtoms() fixer.addMissingHydrogens(7.0) modeller = Modeller(fixer.topology, fixer.positions) modeller.addHydrogens(forcefield=field) modeller.deleteWater() PDBFile.writeModel(modeller.topology, modeller.positions, open(self.shotname+'_h.pdb', 'w'))
def check_hydrogens(molecule, ID): # Check that Hydrogens are in structure if len(molecule.top.select("name == H")) == 0: # If absent, then add Hydrogens using the Amber99sb force-field try: from simtk.openmm.app import PDBFile, Modeller, ForceField pdb = PDBFile(ID + ".pdb") modeller = Modeller(pdb.topology, pdb.positions) forcefield = ForceField('amber99sb.xml','tip3p.xml') modeller.addHydrogens(forcefield) PDBFile.writeFile(modeller.topology, modeller.positions, open(ID + ".pdb", 'w')) molecule = md.load(ID + ".pdb").remove_solvent() except: warnings.warn("""PDB topology missing Hydrogens. Either manually add or install OpenMM through SIMTK to automatically correct.""") pass return molecule
def check_hydrogens(molecule, ID): # Check that Hydrogens are in structure if len(molecule.top.select("name == H")) == 0: # If absent, then add Hydrogens using the Amber99sb force-field try: from simtk.openmm.app import PDBFile, Modeller, ForceField pdb = PDBFile(ID + ".pdb") modeller = Modeller(pdb.topology, pdb.positions) forcefield = ForceField('amber99sb.xml', 'tip3p.xml') modeller.addHydrogens(forcefield) PDBFile.writeFile(modeller.topology, modeller.positions, open(ID + ".pdb", 'w')) molecule = md.load(ID + ".pdb").remove_solvent() except: warnings.warn( """PDB topology missing Hydrogens. Either manually add or install OpenMM through SIMTK to automatically correct.""") pass return molecule
class MoleculeUtil(object): """ A class for managing a molecule defined by a PDB file """ np.random.seed(20) def __init__(self, pdb_path, offset_size=2): # OpenMM init self.pdb_path = pdb_path self.pdb = PDBFile(self.pdb_path) self.forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml') self.modeller = Modeller(self.pdb.topology, self.pdb.positions) # Remove any water that might be present in the PDB file self.modeller.deleteWater() # Add any hydrogens not present self.modeller.addHydrogens(self.forcefield) self.system = self.forcefield.createSystem(self.modeller.topology, nonbondedMethod=PME, nonbondedCutoff=1 * u.nanometer, constraints=HBonds) self.integrator = LangevinIntegrator(300 * u.kelvin, 1 / u.picosecond, 0.002 * u.picoseconds) self.simulation = Simulation(self.modeller.topology, self.system, self.integrator) self.pdb_positions = self.modeller.getPositions() # Initialize bond dictionary and positions for chemcoord self.cc_bonds = {} self.offset_size = offset_size self._init_pdb_bonds() self.set_cc_positions(self.pdb_positions) # Perform initial minimization, which updates self.pdb_positions min_energy, min_positions = self.run_simulation() # Reset the positions after the minimization self.set_cc_positions(self.pdb_positions) self.torsion_indices = self._get_torsion_indices() self.starting_positions = min_positions self.starting_torsions = np.array([ self.zmat.loc[self.torsion_indices[:, 0], 'dihedral'], self.zmat.loc[self.torsion_indices[:, 1], 'dihedral'] ]).T self.seed_offsets() def _add_backbone_restraint(self): # https://github.com/ParmEd/ParmEd/wiki/OpenMM-Tricks-and-Recipes#positional-restraints positions = self.modeller.getPositions() force = CustomExternalForce('k*((x-x0)^2+(y-y0)^2+(z-z0)^2)') force.addGlobalParameter( 'k', 5.0 * u.kilocalories_per_mole / u.angstroms**2) force.addPerParticleParameter('x0') force.addPerParticleParameter('y0') force.addPerParticleParameter('z0') for index, atom in enumerate(self.modeller.topology.atoms()): if atom.name in ('CA', 'C', 'N'): coord = positions[index] force.addParticle(index, coord.value_in_unit(u.nanometers)) self.restraint_force_id = self.system.addForce(force) def _remove_backbone_restraint(self): self.system.removeForce(self.restraint_force_id) def _fix_backbone(self): for index, atom in enumerate(self.modeller.topology.atoms()): if atom.name in ('CA', 'C', 'N'): self.system.setParticleMass(index, 0) def seed_offsets(self): self.offsets = np.random.choice([0, 0, -1, 1], self.starting_torsions.shape) def get_torsions(self): return np.array([ self.zmat.loc[self.torsion_indices[:, 0], 'dihedral'], self.zmat.loc[self.torsion_indices[:, 1], 'dihedral'] ]).T def set_torsions(self, new_torsions): self.zmat.safe_loc[self.torsion_indices[:, 0], 'dihedral'] = new_torsions[:, 0] self.zmat.safe_loc[self.torsion_indices[:, 1], 'dihedral'] = new_torsions[:, 1] def get_offset_torsions(self, scale_factor): """ Calculates and returns new torsion angles based on randomly generated offsets. Args: scale_factor: the relative scale of the offset relative to self.offset_size Returns: The new torsion angles """ total_offset = self.offset_size * scale_factor new_torsions = np.zeros(shape=self.starting_torsions.shape) new_torsions[:, 0] = self.starting_torsions[:, 0] + \ (self.offsets[:, 0] * total_offset) new_torsions[:, 1] = self.starting_torsions[:, 1] + \ (self.offsets[:, 1] * total_offset) return new_torsions def run_simulation(self): """ Run a simulation to calculate the current configuration's energy level. Note that the atoms will likely move somewhat during the calculation, since energy minimization is used. Returns: A tuple of the form (potential_energy, updated_positions) """ # Delete solvent that's based on previous positions cartesian = self.zmat.get_cartesian().sort_index() self.simulation.context.setPositions([ Vec3(x, y, z) for x, y, z in zip(cartesian['x'], cartesian['y'], cartesian['z']) ]) # self._add_backbone_restraint() # self._fix_backbone() self.modeller.addSolvent(self.forcefield, padding=1.0 * u.nanometer) self.simulation.minimizeEnergy(maxIterations=200) state = self.simulation.context.getState(getEnergy=True, getPositions=True) p_energy = state.getPotentialEnergy() positions = state.getPositions(asNumpy=True) # Clean up - remove solvent and backbone restraint (for next iteration) self.modeller.deleteWater() # self._remove_backbone_restraint() return p_energy, positions def _init_pdb_bonds(self): """Construct a dictionary describing the PDB's bonds for chemcoord use""" for index in range(self.modeller.topology.getNumAtoms()): self.cc_bonds[index] = set() for bond in self.modeller.topology.bonds(): self.cc_bonds[bond[0].index].add(bond[1].index) self.cc_bonds[bond[1].index].add(bond[0].index) def set_cc_positions(self, positions): """ Calculates the zmat from an OpenMM modeller Args: positions (list): A list """ cc_df = self._get_cartesian_df(positions) self.cartesian = cc.Cartesian(cc_df) self.cartesian.set_bonds(self.cc_bonds) self.cartesian._give_val_sorted_bond_dict(use_lookup=True) self.zmat = self.cartesian.get_zmat(use_lookup=True) def _get_cartesian_df(self, positions): cc_positions = np.zeros((3, self.modeller.topology.getNumAtoms())) atom_names = [] for index, atom in enumerate(self.modeller.topology.atoms()): pos = positions[index] / u.nanometer atom_names.append(atom.name) cc_positions[:, index] = pos cc_df = pd.DataFrame({ 'atom': atom_names, 'x': cc_positions[0, :], 'y': cc_positions[1, :], 'z': cc_positions[2, :] }) return cc_df def _get_torsion_indices(self): """ Calculates indices into the zmatrix which correspond to phi and psi angles. Args: zmat: the zmatrix specifying the molecule Returns: a numpy.array, with first column as phi_indices, second column as psi_indices """ phi_indices = [] psi_indices = [] for i in range(len(self.zmat.index)): b_index = self.zmat.loc[i, 'b'] a_index = self.zmat.loc[i, 'a'] d_index = self.zmat.loc[i, 'd'] # If this molecule references a magic string (origin, e_x, e_y, e_z, etc) if isinstance(b_index, str) or isinstance( a_index, str) or isinstance(d_index, str): continue # Psi angles if (self.zmat.loc[i, 'atom'] == 'N') & \ (self.zmat.loc[b_index, 'atom'] == 'CA') & \ (self.zmat.loc[a_index, 'atom'] == 'C') & \ (self.zmat.loc[d_index, 'atom'] == 'N'): psi_indices.append(i) elif (self.zmat.loc[i, 'atom'] == 'N') & \ (self.zmat.loc[b_index, 'atom'] == 'C') & \ (self.zmat.loc[a_index, 'atom'] == 'CA') & \ (self.zmat.loc[d_index, 'atom'] == 'N'): psi_indices.append(i) elif (self.zmat.loc[i, 'atom'] == 'C') & \ (self.zmat.loc[b_index, 'atom'] == 'N') & \ (self.zmat.loc[a_index, 'atom'] == 'CA') & \ (self.zmat.loc[d_index, 'atom'] == 'C'): phi_indices.append(i) elif (self.zmat.loc[i, 'atom'] == 'C') & \ (self.zmat.loc[b_index, 'atom'] == 'CA') & \ (self.zmat.loc[a_index, 'atom'] == 'N') & \ (self.zmat.loc[d_index, 'atom'] == 'C'): phi_indices.append(i) return np.array([phi_indices, psi_indices]).T
""" Testing the funnel hypothesis """ from math import pi from simtk.openmm.app import PDBFile, ForceField, Modeller, PME, HBonds from simtk.openmm import LangevinIntegrator from simtk.unit import kelvin, nanometer, picosecond, picoseconds import chemcoord as cc import numpy as np import pandas as pd import util pdb = PDBFile('1ubq.pdb') forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml') modeller = Modeller(pdb.topology, pdb.positions) modeller.addHydrogens(forcefield) system = forcefield.createSystem(modeller.topology, nonbondedMethod=PME, nonbondedCutoff=1 * nanometer, constraints=HBonds) integrator = LangevinIntegrator(300 * kelvin, 1 / picosecond, 0.002 * picoseconds) pdb_bonds = modeller.topology.bonds() atoms = modeller.topology.atoms() positions = modeller.getPositions() cc_bonds = {} cc_positions = np.zeros((3, modeller.topology.getNumAtoms())) atom_names = []