def test_multiligand_building(tmpdir): """ Solvates and membranes a system with multiple ligands """ from vmd import atomsel, molecule from Dabble import DabbleBuilder # Build the system p = str(tmpdir.mkdir("multiligand_build")) b = DabbleBuilder(solute_filename=os.path.join(dir, "B2AR_10ALPs.mae"), output_filename=os.path.join(p, "test.mae"), xy_buf=10., wat_buffer=10., overwrite=True, tmp_dir=p) b.write() # Load the built system m2 = molecule.load("mae", os.path.join(p, "test.mae")) molecule.set_top(m2) # Check the system dimensions are correct solsel = atomsel("protein or resname ACE NMA ALP") prot_x = max(solsel.get("x")) - min(solsel.get("x")) prot_y = max(solsel.get("y")) - min(solsel.get("y")) prot_z = max(solsel.get("z")) - min(solsel.get("z")) assert(abs(molecule.get_periodic(m2)["a"] - prot_x - 20.0) < 0.1) assert(abs(molecule.get_periodic(m2)["b"] - prot_y - 20.0) < 0.1) assert(abs(molecule.get_periodic(m2)["c"] - prot_z - 20.0) < 0.1) # Check all the alprenolols are there assert(len(set(atomsel("resname ALP").get("resid"))) == 10)
def save_representative_frames(sample, outdir, msm=None, clusters=None): """ Saves a frame of protein and ligand representing each cluster in the sampler in a given directory Args: sample (Sampler): MSM trajectory object outdir (str): Location for saved cluster files msm (MarkovStateModel): MSM to use clusters (clusters): Cluster stuff """ if msm is None: msm = sample.mmsm if clusters is None: clusters = sample.mclust protsel = "(protein or resname ACE NMA) and not same fragment as resname %s" \ % " ".join(sample.ligands) fn = open(os.path.join(outdir, "rmsds"), 'w') for cl in msm.mapping_.values(): print("On cluster: %s" % cl) x = visualizers.get_representative_ligand(sample, cl, clusters) if x is None: continue lg, rms = x m, f, l = lg print(" Molid: %d\tFrame: %d\tLigand: %d" % (m, f, l)) fn.write("%s\t%f\n" % (cl, rms)) atomsel("(%s) or (same fragment as residue %d)" % (protsel, l), molid=m, frame=f).write("mae", os.path.join(outdir, "%s.mae" % cl)) fn.close()
def _set_water_names(self): """ Sets the names of water residues and atoms according to the given water model. We do it this way instead of with the GraphMatcher because waters can have a fake bond """ # Sanity check if self.water_model not in self.WATER_NAMES: raise DabbleError("Unsupported water model '%s' with forcefield " "'%s'" % (self.water_model, self.forcefield)) watres = self.WATER_NAMES[self.water_model] if watres not in self.matcher.known_res: raise DabbleError("Water resname '%s' for model '%s' not defined " "in topology files" % (watres, self.water_model)) # Set consistent residue and atom names, crystal waters # can be named HOH, etc residues = set(atomsel("water").residue) # If no water, nothing to do if not residues: return watsel = "residue %s" % ' '.join(str(_) for _ in residues) atomsel(watsel).resname = self.WATER_NAMES[self.water_model] atomsel("%s and noh" % watsel).name = self.WATER_O_NAME atomsel("%s and not noh" % watsel).name = self.WATER_H_NAMES * len(residues)
def general_water(tmpdir, writerclass, forcefield, water_model): """ Tests water generally """ m = molecule.load("pdb", os.path.join(dir, "tip3.pdb")) p = str(tmpdir) w = writerclass(molid=m, tmp_dir=p, forcefield=forcefield, water_model=water_model) w.write(os.path.join(p, "test")) if os.path.isfile(os.path.join(p, "test.prmtop")): tm = molecule.load("parm7", os.path.join(p, "test.prmtop"), "rst7", os.path.join(p, "test.inpcrd")) else: tm = molecule.load("psf", os.path.join(p, "test.psf"), "pdb", os.path.join(p, "test.pdb")) molecule.set_top(tm) watsel = "water or resname TIP3 TP4E TP4 SPC SPCE" assert len(set(atomsel(watsel).residue)) == 1 assert len(set(atomsel(watsel).resid)) == 1 if water_model == "tip4e": assert len(atomsel()) == 4 else: # SPC or TIP3 assert len(atomsel()) == 3
def _set_atom_names(self): """ Sets the atom and residue names for a GROMACS-style force field Args: molid (int): Molecule ID """ # Rename all residues residues = set(atomsel("all", molid=self.molid).residue) n_res = len(residues) while residues: if len(residues) % 500 == 0: print("Renaming residues.... %.0f%% \r" % (100. - 100 * len(residues) / float(n_res)), flush=True) residue = residues.pop() sel = atomsel("residue %s" % residue) resnames, atomnames = self.matcher.get_names(sel, print_warning=False) if not resnames: rgraph = self.matcher.parse_vmd_graph(sel)[0] self.matcher.write_dot(rgraph, "rgraph.dot") raise ValueError("Unknown residue %s:%d" % (sel.resname[0], sel.resid[0])) self._apply_naming_dictionary(resnames=resnames, atomnames=atomnames) sel.user = 1.0
def _write_solvent(self): """ Writes ions. Renames waters according to the water model and writes multiple PDB files to bypass PDB file format issues. Returns: (list of str): PDB filenames that were written """ # Match up ion names to topology templates written = [] allions = [] for resname in set(atomsel("numbonds 0").resname): allions.extend(self._rename_by_resname(resname, renumber=True)) # Dump to a temporary PDB file if allions: _, temp = tempfile.mkstemp(suffix='.pdb', prefix='amber_ion', dir=self.tmp_dir) os.close(_) ionsel = atomsel("residue %s" % ' '.join(str(_) for _ in allions)) ionsel.write('pdb', temp) ionsel.user = 0.0 written.append(temp) # Now write water PDBs, waters are already named written.extend(self._write_water_pdbs()) return written
def _write_unorderedindex_waters(self, residues, molid): """ Renumbers and sorts the specified waters manually. This is much less efficient but is necessary in cases where atoms within a water molecule are not sequential in index, preventing quick renaming with VMD. Identify problem waters, then call this on them. It'll write its own psf_wat_* file with just those waters, minimizing inefficiency. Args: residues (list of int): Problem water molecules molid (int): VMD molecule ID to write Returns: (str): Filename where waters are written """ f, temp = tempfile.mkstemp(suffix='_indexed.pdb', prefix='psf_wat_', dir=self.tmp_dir) idx = 1 with os.fdopen(f, 'w') as fileh: for ridx, residue in enumerate(residues): res = atomsel('residue %d' % residue, molid=molid) for i in res.index: a = atomsel('index %d' % i, molid) fileh.write(self.get_pdb_line(a, idx, ridx + 1)) idx += 1 fileh.write('END\n') return temp
def test_basic_getset(file_3frames): m = molecule.load("pdb", file_3frames) sel = atomsel(selection="protein", molid=m, frame=1) assert sel.frame == 1 assert sel.molid == m assert len(sel.bonds) == 10 assert str(sel) == "protein" assert repr(sel) == "atomsel('protein', molid=%d, frame=%d)" % (m, 1) # Default selections molecule.set_frame(m, 2) molecule.set_top(m) sel = atomsel() assert repr(sel) == "atomsel('all', molid=%d, frame=-1)" % m # Set something without a setter with pytest.raises(AttributeError): sel.molid = m+1 # Invalid selection text with pytest.raises(ValueError): atomsel(selection="cannot be parsed") molecule.delete(m) # Selection on deleted molecule with pytest.raises(ValueError): assert sel.frame == 1 with pytest.raises(ValueError): assert sel.molid == m with pytest.raises(ValueError): assert sel.bonds == []
def test_basic_getset(file_3frames): m = molecule.load("pdb", file_3frames) sel = atomsel(selection="protein", molid=m, frame=1) assert sel.frame == 1 assert sel.molid == m assert len(sel.bonds) == 10 assert str(sel) == "protein" assert repr(sel) == "atomsel('protein', molid=%d, frame=%d)" % (m, 1) # Default selections molecule.set_frame(m, 2) molecule.set_top(m) sel = atomsel() assert repr(sel) == "atomsel('all', molid=%d, frame=-1)" % m # Set something without a setter with pytest.raises(AttributeError): sel.molid = m + 1 # Invalid selection text with pytest.raises(ValueError): atomsel(selection="cannot be parsed") molecule.delete(m) # Selection on deleted molecule with pytest.raises(ValueError): assert sel.frame == 1 with pytest.raises(ValueError): assert sel.molid == m with pytest.raises(ValueError): assert sel.bonds == []
def load_solute(filename, tmp_dir): """ Loads a molecule input file, guessing the format from the extension. Args: filename (str): Filename to load tmp_dir (str): Directory to put temporary files in Returns: (int) VMD molecule ID that was loaded Raises: ValueError if filetype is currently unsupported """ if len(filename) < 3: raise ValueError("Cannot determine filetype of input file '%s'" % filename) ext = filename[-3:] if ext == 'mae': molid = molecule.load('mae', filename) elif ext == 'dms': molid = molecule.load('dms', filename) elif ext == 'pdb': # Need to convert to MAE so concatenation will work later temp_mae = tempfile.mkstemp(suffix='.mae', prefix='dabble_input', dir=tmp_dir)[1] molid = molecule.load('pdb', filename) atomsel('all').write('mae', temp_mae) molecule.delete(molid) molid = molecule.load('mae', temp_mae) else: raise ValueError("Filetype '%s' currently unsupported " "for input protein" % ext) return molid
def write_ct_blocks(molid, sel, output_filename, tmp_dir): """ Writes a mae format file containing the specified selection. Args: molid (int): VMD molecule ID to write sel (str): the selection to write output_filename (str): the file to write to, including .mae extension tmp_dir (str): Directory to put files in Returns: length (int): the number of CT blocks written """ users = sorted(set(atomsel(sel, molid=molid).get('user'))) filenames = [(tempfile.mkstemp(suffix='.mae', prefix='dabble_tmp_user', dir=tmp_dir))[1] for _ in users] length = len(users) for i, filen in zip(users, filenames): tempsel = atomsel('user %f and (%s)' % (i, sel), molid=molid) sel2 = atomsel('index ' + \ ' '.join([str(s) for s in set(tempsel.get('index'))]), molid=molid) sel2.set('user', 0.0) sel2.write('mae', filen) # Option lets us specify if we should write a pdb/psf or just a mae file # Either way it writes a temp mae file, hacky but it works concatenate_mae_files(output_filename, input_filenames=filenames) # Clean up for filename in filenames: os.remove(filename) # delete temporary files return length
def rmsd_vmd(self,atomselect): rmsd_array=[] # use frame 0 for the reference reference = atomsel(selection=atomselect, molid=self.molID, frame=0) reference1 = atomsel(selection=atomselect, molid=self.molID, frame=1) x=reference.rmsd(selection=reference1) print (x)
def get_residue_insert_mem_per_frame(self,frame,membrane,fosfolipid_head_select,protein_select): inserted_residues=[] protein = atomsel(selection=protein_select, molid=self.molID, frame=frame) resid_center=protein.centerperresidue() resid_z=list(map(lambda x: x, resid_center)) membrane = atomsel(selection=membrane, molid=self.molID, frame=frame) #outerleaf name_P_down=atomsel(selection=fosfolipid_head_select+" and z<"+str(membrane.center(membrane.mass)[2]) ,molid=self.molID, frame=frame) #inner leaf name_P_up=atomsel(selection=fosfolipid_head_select+" and z>"+str(membrane.center(membrane.mass)[2]) ,molid=self.molID, frame=frame) x_down=np.asarray(name_P_down.x).transpose() y_down=np.asarray(name_P_down.z).transpose() p_down = np.polyfit(x_down, y_down, 1) x_up=np.asarray(name_P_up.x).transpose() y_up=np.asarray(name_P_up.z).transpose() p_up = np.polyfit(x_up, y_up, 1) for number_resid in range(len(resid_z)): x_residue=resid_z[number_resid][0] z_down_ajuste = p_down[0]*x_residue + p_down[1] z_up_ajuste = p_up[0]*x_residue + p_up[1] if resid_z[number_resid][2]<z_up_ajuste and resid_z[number_resid][2]>z_down_ajuste: inserted_residues.append(number_resid) return inserted_residues
def rmsd_time_references(self,atomselect,references1): rmsd_array=[] # use frame 0 for the reference reference = atomsel(selection=atomselect, molid=self.molID, frame=0) reference2 = atomsel(selection=references1, molid=self.molID, frame=0) #compare = atomsel(atomselect) #set reference [atomselect $mol "protein" frame 0] # the frame being compared #set compare [atomselect $mol "protein"] for frame in range(Trajectory.num_frames(self)): #protein = atomsel(selection="protein", molid=molid, frame=frame) # the frame being compared compare = atomsel(selection=atomselect, molid=self.molID, frame=frame) compare2 = atomsel(selection=references1, molid=self.molID, frame=frame) #set trans_mat [measure fit $compare $reference] trans_mat=atomsel.fit(compare,reference) # do the alignment #compare.move(trans_mat) compare2.move(trans_mat) #$compare move $trans_mat # compute the RMSD #set rmsd [measure rmsd $compare $reference] rmsd_array.append(atomsel.rmsd(compare2,reference2)) return rmsd_array
def _write_ion_blocks(self): """ Writes a PDB file containing correctly named ions for use by psfgen, and instructs psfgen to use it in TCL code. """ # Put our molecule on top to simplify atom selection language old_top = molecule.get_top() molecule.set_top(self.molid) # Select all ions allions = [] for resname in set(atomsel("numbonds 0").resname): allions.extend(self._rename_by_resname(resname, renumber=True)) # Stop if no ions were found if not allions: return # Save ions as pdb allsel = atomsel("residue %s" % " ".join(str(_) for _ in allions)) allsel.resid = range(len(allsel)) allsel.user = 0.0 _, temp = tempfile.mkstemp(suffix=".pdb", prefix="psf_ions_", dir=self.tmp_dir) os.close(_) allsel.write("pdb", temp) self.psfgen.add_segment(segid="I", pdbfile=temp) self.psfgen.read_coords(segid="I", filename=temp) molecule.set_top(old_top)
def test_multiligand_building(tmpdir): """ Solvates and membranes a system with multiple ligands """ from dabble import DabbleBuilder p = str(tmpdir) # Build the system b = DabbleBuilder(solute_filename=os.path.join(dir, "B2AR_10ALPs.mae"), output_filename=os.path.join(p, "test.mae"), xy_buf=10., wat_buffer=10., overwrite=True, tmp_dir=p) b.write() # Load the built system m2 = molecule.load("mae", os.path.join(p, "test.mae")) molecule.set_top(m2) # Check the system dimensions are correct solsel = atomsel("protein or resname ACE NMA ALP") prot_x = max(solsel.x) - min(solsel.x) prot_y = max(solsel.y) - min(solsel.y) prot_z = max(solsel.z) - min(solsel.z) assert abs(molecule.get_periodic(m2)["a"] - prot_x - 20.0) < 0.1 assert abs(molecule.get_periodic(m2)["b"] - prot_y - 20.0) < 0.1 assert abs(molecule.get_periodic(m2)["c"] - prot_z - 20.0) < 0.1 # Check all the alprenolols are there assert len(set(atomsel("resname ALP").resid)) == 10 assert len(atomsel("resname ALP")) == 420 molecule.delete(m2)
def test_atomsel_update(file_3frames): m = molecule.load("pdb", file_3frames) atomsel("resid 1", m, frame=2).user = 1.0 sel = atomsel("user 1.0", molid=m, frame=-1) assert sel.frame == -1 assert sel.index == list(range(10)) altersel = atomsel("index 9", m, frame=2) altersel.user = 0.0 assert atomsel("index 8 9", m, frame=2).user == approx([1.0, 0.0]) assert atomsel("index 8 9", m, frame=0).user == approx([0.0, 0.0]) # Selection should only change if the frame is set to update molecule.set_frame(m, 2) assert sel.index == list(range(10)) sel.update() assert sel.index == list(range(9)) # Now put it back to another frame sel.frame = 0 assert sel.frame == 0 assert sel.index == list(range(9)) sel.update() assert sel.index == [] molecule.delete(m)
def check_result(format, outdir, solvent=True, lipid=True): if format == "prmtop": m2 = molecule.load("parm7", os.path.join(outdir, "test.prmtop"), "rst7", os.path.join(outdir, "test.inpcrd")) elif format == "psf": m2 = molecule.load("psf", os.path.join(outdir, "test.psf"), "pdb", os.path.join(outdir, "test.pdb")) # Check by reading .top file, as VMD won't parse it and .gro doesn't # have bond information elif format == "gro": return check_gro(os.path.join(outdir, "test.top"), lipid=lipid) elif format == "lammps": return check_lammps(os.path.join(outdir, "test.dat")) molecule.set_top(m2) assert len(set(atomsel("protein").resid)) == 282 assert len(set(atomsel("resname ACE NMA NME").resid)) == 4 if solvent: assert len(atomsel("water")) == 32106 assert len(atomsel("same fragment as lipid")) == 12194 # Check for the corrrect number of alprenolols assert len(atomsel("resname ALP")) == 420 assert len(set(atomsel("resname ALP").resid)) == 10 assert any(_ in set(atomsel("resname ALP").name) for _ in ["O09", "OX"]) assert "O1" not in set(atomsel("resname ALP").name) # Check coordinates are unique (not zero) assert len(atomsel("resname ALP")) == 420 assert 0.0 not in atomsel("resname ALP").x molecule.delete(m2)
def write_ct_blocks(molid, sel, output_filename, tmp_dir): """ Writes a mae format file containing the specified selection. Args: molid (int): VMD molecule ID to write sel (str): the selection to write output_filename (str): the file to write to, including .mae extension tmp_dir (str): Directory to put files in Returns: length (int): the number of CT blocks written """ users = sorted(set(atomsel(sel, molid=molid).user)) filenames = [(tempfile.mkstemp(suffix='.mae', prefix='dabble_tmp_user', dir=tmp_dir))[1] for _ in users] length = len(users) for i, filen in zip(users, filenames): tempsel = atomsel('user %f and (%s)' % (i, sel), molid=molid) sel2 = atomsel('index ' + \ ' '.join([str(s) for s in set(tempsel.index)]), molid=molid) sel2.user = 0.0 sel2.write('mae', filen) # Option lets us specify if we should write a pdb/psf or just a mae file # Either way it writes a temp mae file, hacky but it works concatenate_mae_files(output_filename, input_filenames=filenames) # Clean up for filename in filenames: os.remove(filename) # delete temporary files return length
def _set_solute_sel(self, molid): """ Gets the list of resids uniquely corresponding to the in the system and sets the value of the solute_sel attribute. Does this separately by chain since sometimes resids can be the same across different chains. This selection can be used to pull out the solute once other things are added later. This assumes that the solute is the only thing in the system right now. Args: molid (int) : VMD molecule ID to get the selection from Returns: (str) : VMD atom selection for these residues """ # Temporary fix for chain W in input file if len(atomsel('chain W')): print("WARNING: Renaming crystal water chain to X, temporary bugfix") atomsel('chain W').set('chain', 'X') chains = set(atomsel('all', molid=molid).get('chain')) sel = "" while len(chains): chn = chains.pop() # have to handle first separately because of or sel += "(chain %s and resid " % (chn) + \ " ".join(["'%d'" % i for i in \ set(atomsel('chain %s' % chn, molid=molid).get('resid'))]) + \ ")" if len(chains): sel += " or " self.solute_sel = sel return sel
def _set_solute_sel(self, molid): """ Gets the list of resids uniquely corresponding to the in the system and sets the value of the solute_sel attribute. Does this separately by chain since sometimes resids can be the same across different chains. This selection can be used to pull out the solute once other things are added later. This assumes that the solute is the only thing in the system right now. Args: molid (int) : VMD molecule ID to get the selection from Returns: (str) : VMD atom selection for these residues """ # Temporary fix for chain W in input file if len(atomsel('chain W')): print( "WARNING: Renaming crystal water chain to X, temporary bugfix") atomsel('chain W').set('chain', 'X') chains = set(atomsel('all', molid=molid).get('chain')) sel = "" while len(chains): chn = chains.pop() # have to handle first separately because of or sel += "(chain %s and resid " % (chn) + \ " ".join(["'%d'" % i for i in \ set(atomsel('chain %s' % chn, molid=molid).get('resid'))]) + \ ")" if len(chains): sel += " or " self.solute_sel = sel return sel
def save_initial_molecule(self, molid, sampled, repindex): """ Saves a single molecule added thing. Names it according to the index and the sampled cluster. """ # Write out to the output directory indicating the cluster # that was selected in the file name outnam = os.path.join(self.outdir, "%d_init_%d.mae" % (repindex, sampled)) # Since we operate only off stripped prmtops, this should contain # the full system minus any dabble additions atomsel("all").write("mae", outnam) # Start a slurm job that will add additional ligands and dabble old_dir = os.getcwd() os.chdir(self.config["system"]["rootdir"]) subprocess.call([ "sbatch", "--time=2:00:00", "--partition=rondror", "--tasks=2", "--cpus-per-task=1", "--mem=8GB", "--job-name=%s_dabbler_G%d-%d" % (self.config["system"]["jobname"], self.config.getint("production", "generation") + 1, repindex), "--export=INPUT=%s,CONFIG=%s" % (outnam, os.path.join(self.config["system"]["rootdir"], "sampler.cfg")), "--output=%s" % os.path.join(self.outdir, "dabble_%d.log" % repindex), "--open-mode=append", os.path.join(self.config["system"]["scriptdir"], "ligand_adder.py") ]) os.chdir(old_dir)
def _write_ligands(self): """ Writes any remaining user=1.0 residues each to a pdb file. Renumbers the residues along the way. Previously this was done with a mol2 file, but tleap takes charges from there and we will use a topology library file to get the connectivity correct. Returns: (list of 2-tuple): ilename of each ligand, unit name of ligand """ idx = 1 pdbs = [] for residue in set(atomsel("user 1.0").get("residue")): temp = tempfile.mkstemp(suffix='.pdb', prefix='amber_extra', dir=self.tmp_dir)[1] sel = atomsel("residue %d" % residue) sel.set('user', 0.0) sel.set('resid', idx) sel.write("pdb", temp) unit = self.matcher.get_unit(sel) pdbs.append((temp, unit)) idx += 1 return pdbs
def go(self): """ Actually runs """ # Set the user field of actually placed ligand to nonzero # this is the prettiest way to do ligand placement, but the user # field doesn't save. We temporarily communicate this with zero cooods atomsel("all").set("user", 1.0) atomsel("same fragment as resname %s and (x=0. and y=0. and z=0.)" % self.config["system"]["ligands"].replace(",", " "), molid=self.molid).set("user", 0.0) # Add ligands and Dabble with VmdSilencer( output=os.path.join(self.config["system"]["rootdir"], "systems", str(self.gen + 1), "generator.log")): self.add_ligands_and_dabble() # Start the simulation self.start_jobs() # Check for output files from other adders. If we're all done, # increment generation for idx in range(1, self.config.getint("model", "samplers") + 1): if not os.path.isfile(os.path.join(self.outdir, "%d.prmtop" % idx)): return self.config["production"]["generation"] = str(self.gen + 1) with open(os.environ.get("CONFIG"), 'w') as configfile: self.config.write(configfile)
def test_atomsel_fit_move(file_3nob): m1 = molecule.load("mae", file_3nob) m2 = molecule.load("mae", file_3nob) sel1 = atomsel("protein", m1) sel2 = atomsel("protein", m2) # Move one selection over assert sel1.x[0] == approx(sel2.x[0]) sel1.moveby((1.0, 0.0, 0.0)) assert sel1.x[0] == approx(sel2.x[0] + 1.0) sel1.moveby([1.0, 0.0, 0.0]) assert sel1.x[0] == approx(sel2.x[0] + 2.0) assert sel1.y[0] == approx(sel2.y[0]) assert sel1.z[0] == approx(sel2.z[0]) # Fit, no weights fit1 = sel1.fit(sel2) assert fit1[0] == approx(1.0, abs=1e-5) # Fit, with weights fit0 = sel1.fit(selection=sel2, weight=[0.0] + [1.0]*(len(sel2)-1)) assert fit0 == approx((1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -2.0, 0.0, 0.0, 1.0)) # Test selection invertible fit2 = np.reshape(sel2.fit(sel1), (4,4)) assert np.linalg.inv(fit2) == approx(np.reshape(fit1, (4,4))) # Move sel1.move(fit1) assert sel1.x[0] == sel2.x[0] # Move with a numpy array - all zeros nans the array? sel1.move(np.zeros((16,))) assert sel1.y == approx(sel1.x, nan_ok=True) assert sel1.y != approx(sel1.x, nan_ok=False) # Operations on empty selection isel = atomsel("resname NOPE", m1) with pytest.raises(ValueError): isel.moveby((1., 2., 3.)) with pytest.raises(ValueError): isel.move(fit1) with pytest.raises(ValueError): isel.fit(isel) molecule.delete(m1) with pytest.raises(ValueError): sel1.fit(sel2) molecule.delete(m2) with pytest.raises(ValueError): sel1.moveby((1,2,3)) with pytest.raises(ValueError): sel2.move(fit1)
def test_atomsel_fit_move(file_3nob): m1 = molecule.load("mae", file_3nob) m2 = molecule.load("mae", file_3nob) sel1 = atomsel("protein", m1) sel2 = atomsel("protein", m2) # Move one selection over assert sel1.x[0] == approx(sel2.x[0]) sel1.moveby((1.0, 0.0, 0.0)) assert sel1.x[0] == approx(sel2.x[0] + 1.0) sel1.moveby([1.0, 0.0, 0.0]) assert sel1.x[0] == approx(sel2.x[0] + 2.0) assert sel1.y[0] == approx(sel2.y[0]) assert sel1.z[0] == approx(sel2.z[0]) # Fit, no weights fit1 = sel1.fit(sel2) assert fit1[0] == approx(1.0, abs=1e-5) # Fit, with weights fit0 = sel1.fit(selection=sel2, weight=[0.0] + [1.0] * (len(sel2) - 1)) assert fit0 == approx((1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -2.0, 0.0, 0.0, 1.0)) # Test selection invertible fit2 = np.reshape(sel2.fit(sel1), (4, 4)) assert np.linalg.inv(fit2) == approx(np.reshape(fit1, (4, 4))) # Move sel1.move(fit1) assert sel1.x[0] == sel2.x[0] # Move with a numpy array - all zeros nans the array? sel1.move(np.zeros((16, ))) assert sel1.y == approx(sel1.x, nan_ok=True) assert sel1.y != approx(sel1.x, nan_ok=False) # Operations on empty selection isel = atomsel("resname NOPE", m1) with pytest.raises(ValueError): isel.moveby((1., 2., 3.)) with pytest.raises(ValueError): isel.move(fit1) with pytest.raises(ValueError): isel.fit(isel) molecule.delete(m1) with pytest.raises(ValueError): sel1.fit(sel2) molecule.delete(m2) with pytest.raises(ValueError): sel1.moveby((1, 2, 3)) with pytest.raises(ValueError): sel2.move(fit1)
def porcentaje_contact_radio_gyrations(self,atomselect1,atomselect2): for frame in range(Trajectory.num_frames(self)): #protein = atomsel(selection="protein", molid=molid, frame=frame) sel1 = atomsel(selection=atomselect1, molid=self.molID, frame=frame) sel2 =atomsel(selection=atomselect2, molid=self.molID, frame=frame) sel1_z = np.array((sel1.center(sel1.mass)[2])) sel2_z= np.array((sel2.center(sel2.mass)[2])) distance_mass_weight.append(np.linalg.norm(sel2_z-sel1_z))
def _write_protein(self): """ Writes the protein chain to a pdb file. Writes each fragment to a separate file. This is somewhat inelegant but necessary in order to have disulfide bonds between different fragments have sane residue numbering in leap. Returns: list of (str, str) Name of pdb files written, and UNIT sequence inside """ pdbinfos = [] psel = atomsel("user 1.0 and resname %s" % " ".join(self.matcher._acids)) # Start the protein numbering from 1, as it's lost in the prmtop anyway. # Make resids increase across chains/fragments too, so that bond section # for covalent modifications is simple. fragres = 1 for i, frag in enumerate(sorted(set(psel.get("fragment")))): temp = tempfile.mkstemp(suffix='_prot.pdb', prefix='amber_prot_', dir=self.tmp_dir)[1] # Now write out all the resides to a pdb file resseq = [] with open(temp, 'w') as fileh: idx = 1 # Grab resids again since they may have updated # Check for multiple residues with the same resid (insertion codes) for resid in sorted(set(atomsel("fragment '%s'" \ % frag).get("resid"))): selstr = "fragment '%s' and resid '%d' " \ "and user 1.0" % (frag, resid) for residue in sorted(set(atomsel(selstr).get("residue"))): sel = atomsel("residue %d" % residue) sel.set('resid', fragres) sel.set('segname', str(i)) sel.set('user', 0.0) idx = self._write_residue(sel, fileh, idx, hetatm=False) resseq.append(sel.get("resname")[0]) fragres += 1 fileh.write("END\n") fileh.close() # Insert line breaks every 100 AA to avoid buffer overflow in tleap seqstring = "" for i, res in enumerate(resseq): seqstring += " %s" % res if i % 100 == 0: seqstring += "\n" pdbinfos.append((temp, seqstring)) return pdbinfos
def _trim_water(self, molid): """ Removes water residues in the +- Z direction in the system. Used to chop off extra waters from the protein that are past a certain cutoff distance. Changes are apparent on next write. Args: molid (int): VMD molecule id to use Returns: int Number of atoms deleted Raises: ValueError if water buffer is None """ if not self.opts.get('wat_buffer'): raise ValueError("Water buffer undefined") # Remove waters in the Z direction # Check if we are trimming to absolute size and set z buf if so total = 0 zcoord = atomsel(self.solute_sel).get('z') total = _remove_residues('(not (%s) and not (%s)) and noh and z > %f' % \ (self.solute_sel, self.opts['lipid_sel'], self._zmax), molid=molid) total += _remove_residues('(not (%s) and not (%s)) and noh and z < %f' % (self.solute_sel, self.opts['lipid_sel'], self._zmin), molid=molid) # Trim in the XY direction if it's a pure water system # lipid trimming is done in trim_xy_residues and takes into account # lipid center, etc. if self.water_only: xcoord = atomsel(self.solute_sel).get('x') buf = (self.size[0] - max(xcoord) + min(xcoord))/2. total += _remove_residues('(not (%s)) and noh and x > %f' % (self.solute_sel, max(xcoord) + buf), molid=molid) total += _remove_residues('(not (%s)) and noh and x < %f' % (self.solute_sel, min(xcoord) - buf), molid=molid) ycoord = atomsel(self.solute_sel).get('y') buf = (self.size[1] - max(ycoord) + min(ycoord))/2. total += _remove_residues('(not (%s)) and noh and y > %f' % (self.solute_sel, max(ycoord) + buf), molid=molid) total += _remove_residues('(not (%s)) and noh and y < %f' % (self.solute_sel, min(ycoord) - buf), molid=molid) return total
def get_disulfide(self, selection, molid): """ Checks if the selection corresponds to a cysteine in a disulfide bond. Sets the patch line appropriately and matches atom names using a subgraph match to the normal cysteine residue Args: selection (VMD atomsel): Selection to check molid (int): VMD molecule ID to look for other CYS in Returns: resnames (dict int -> str) Residue name translation dictionary atomnames (dict int -> str) Atom name translation dictionary conect (int) Residue this one is connected to """ rgraph, _ = self.parse_vmd_graph(selection) # Sanity check if not self.known_res.get("CYX"): raise ValueError("CYX undefined. Check forcefields!") # Check for the 3 join atoms corresponding to the disulfide bonds externs = self.get_extraresidue_atoms(selection) if len(externs) != 3: return (None, None, None) # With the AMBER format, the CYX residue should be a subgraph of this # residue as the only difference is the _join bond graph = self.known_res.get("CYX") matcher = isomorphism.GraphMatcher(rgraph, graph, \ node_match=self._check_atom_match) if matcher.subgraph_is_isomorphic(): match = next(matcher.match()) else: return (None, None, None) # Generate naming dictionaries to return nammatch = dict((i, graph.node[match[i]].get("atomname")) \ for i in match.keys() if \ graph.node[match[i]].get("residue") == "self") resmatch = dict((i, graph.node[match[i]].get("resname")) \ for i in match.keys() if \ graph.node[match[i]].get("residue") == "self") # Now we know it's a cysteine in a disulfide bond # Identify which resid and fragment corresponds to the other cysteine partners = [n for n in externs if \ atomsel("index %d" % n, molid=molid).get("element")[0] == "S"] if not partners: raise ValueError("3 bonded Cys %d isn't a valid disulfide!" % selection.get('resid')[0]) osel = atomsel("index %d" % partners[0], molid=molid) conect = osel.get("residue")[0] return (resmatch, nammatch, conect)
def get_disulfide(self, selection, molid): """ Checks if the selection corresponds to a cysteine in a disulfide bond. Sets the patch line appropriately and matches atom names using a subgraph match to the normal cysteine residue Args: selection (VMD atomsel): Selection to check molid (int): VMD molecule ID to look for other CYS in Returns: resnames (dict int -> str) Residue name translation dictionary atomnames (dict int -> str) Atom name translation dictionary conect (int) Residue this one is connected to """ rgraph, _ = self.parse_vmd_graph(selection) # Sanity check if not self.known_res.get("CYX"): raise DabbleError("CYX undefined. Check forcefields!") # Check for the 3 join atoms corresponding to the disulfide bonds externs = self.get_extraresidue_atoms(selection) if len(externs) != 3: return (None, None, None) # With the AMBER format, the CYX residue should be a subgraph of this # residue as the only difference is the _join bond graph = self.known_res.get("CYX") matcher = isomorphism.GraphMatcher(rgraph, graph, \ node_match=self._check_atom_match) if matcher.subgraph_is_isomorphic(): match = next(matcher.match()) else: return (None, None, None) # Generate naming dictionaries to return nammatch = dict((i, graph.node[match[i]].get("atomname")) \ for i in match.keys() if \ graph.node[match[i]].get("residue") == "self") resmatch = dict((i, graph.node[match[i]].get("resname")) \ for i in match.keys() if \ graph.node[match[i]].get("residue") == "self") # Now we know it's a cysteine in a disulfide bond # Identify which resid and fragment corresponds to the other cysteine partners = [n for n in externs if \ atomsel("index %d" % n, molid=molid).get("element")[0] == "S"] if not partners: raise DabbleError("3 bonded Cys %d isn't a valid disulfide!" % selection.get('resid')[0]) osel = atomsel("index %d" % partners[0], molid=molid) conect = osel.get("residue")[0] return (resmatch, nammatch, conect)
def test_load_mae(file_rho): from vmd import molecule, atomsel assert callable(atomsel) molecule.load('mae', file_rho) chrg = set(atomsel().get('charge')) assert chrg == set([0.0, 1.0, -1.0]) ins = set(atomsel().get('insertion')) assert set(_.strip() for _ in ins) == set(['', 'A'])
def test_load_mae(): from vmd import molecule, atomsel assert callable(atomsel) molecule.load('mae', os.path.join(dir, "rho_test.mae")) chrg = set(atomsel().get('charge')) assert chrg == set([0.0, 1.0, -1.0]) ins = set(atomsel().get('insertion')) assert ins == set([' ', 'A'])
def test_multiple_insertion_codes(tmpdir): from dabble.param import CharmmWriter # Generate the file p = str(tmpdir) molid = molecule.load("mae", os.path.join(dir, "3PTB_1lig_prepped.mae")) w = CharmmWriter(molid=molid, tmp_dir=p, forcefield="charmm", hmr=False, override_defaults=False) w.write(os.path.join(p, "test")) # Load the output file and start checking it m2 = molecule.load("psf", os.path.join(p, "test.psf"), "pdb", os.path.join(p, "test.pdb")) molecule.set_top(m2) # Check the entire protein is there and connected assert len(atomsel("protein or resname ACE NMA NME")) == 3229 assert len(set(atomsel("protein or resname ACE NMA NME").fragment)) == 1 # Check the calcium ion is present assert atomsel("element Ca").resname == ["CAL"] # Check residues with insertion codes assert set(atomsel("resid 184").resname) == set(["GLY", "TYR"]) assert set(atomsel("resid 188").resname) == set(["GLY", "LYS"]) assert set(atomsel("resid 221").resname) == set(["ALA", "GLN"]) assert set(atomsel("resid 245").resname) == set(["ASN", "NMA"]) assert set(atomsel("all").insertion) == set([" ", "A"]) # Check the ligand is there assert len(atomsel("resname BAMI")) == 18
def _rename_atoms_amber(self): """ Matches up atom names with those in the provided topologies and sets the atom and residue names correctly in the built molecule. Handles all non-lipid atoms. Sets the user field of all atoms to 1.0 to track which things have been written. Returns: (set of tuples (int,int)): Residue #s of disulfide or otherwise noncanonically linked residues Raises: ValueError if a residue definition could not be found """ self._set_water_names() nonlips = set( atomsel("not (water or %s)" % self.lipid_sel, molid=self.molid).residue) n_res = len(nonlips) conect = set() # Atom indices bound to noncanonical residues while nonlips: if len(nonlips) % 500 == 0: print("Renaming residues.... %.0f%% \r" % (100. - 100 * len(nonlips) / float(n_res)), flush=True) residue = nonlips.pop() sel = atomsel("residue %s" % residue) resnames, atomnames = self.matcher.get_names(sel, print_warning=False) # Check if it's a linkage to another amino acid if not resnames: resnames, atomnames, other = self.matcher.get_linkage( sel, self.molid) if not resnames: rgraph = self.matcher.parse_vmd_graph(sel)[0] self.matcher.write_dot(rgraph, "rgraph.dot") raise DabbleError( "ERROR: Could not find a residue definition " "for %s:%s" % (sel.resname[0], sel.resid[0])) print( "\tBonded residue: %s:%d -> %s" % (sel.resname[0], sel.resid[0], list(resnames.values())[0])) conect.add(other) # Do the renaming self._apply_naming_dictionary(resnames=resnames, atomnames=atomnames) atomsel('all').user = 1.0 print("\n", flush=True) return conect
def test_atomsel_minmax_center(file_3frames): m = molecule.load("pdb", file_3frames) sel = atomsel(selection="protein", molid=m, frame=1) alasel = atomsel("resname ALA", molid=m) # Minmax without radii assert sel.minmax() == (approx( (2.497, 0.724, -0.890)), approx((6.009, 4.623, 2.131))) # Minmax with radii sel.radius = 10.0 assert sel.minmax(radii=True) == (approx( (2.497 - 10., 0.724 - 10., -0.890 - 10.)), approx((6.009 + 10., 4.623 + 10., 2.131 + 10.))) assert sel.center(weight=None) == approx((4.040, 2.801, 0.492), rel=1e-3) assert sel.center(weight=[0.] * 9 + [1.]) == atomsel("index 9", m).center() # Wrong number of weights with pytest.raises(TypeError): sel.center(weight=3.0) with pytest.raises(ValueError): sel.center([3., 2.]) sel = atomsel("all") assert sel.centerperresidue() == [ approx((4.040, 2.801, 0.492), rel=1e-3), approx((7.260, 3.987, 0.000), rel=1e-3) ] assert sel.centerperresidue(weight=None)[0] == alasel.centerperresidue()[0] # Centerperresidue with weights with pytest.raises(TypeError): sel.centerperresidue(weight=[True] * len(sel)) with pytest.raises(TypeError): sel.centerperresidue(3) with pytest.raises(ValueError): sel.centerperresidue([1.0, 2.0]) weights = [1.0] * 10 + [0.0] * 6 assert sel.centerperresidue(weight=weights)[0] == approx(alasel.center(), rel=1e-3) assert all([math.isnan(x) for x in sel.centerperresidue(weights)[1]]) molecule.delete(m) # Operations on deleted molecule with pytest.raises(ValueError): sel.minmax(radii=False) with pytest.raises(ValueError): sel.centerrperres() with pytest.raises(ValueError): sel.center()
def test_atomsel_minmax_center(file_3frames): m = molecule.load("pdb", file_3frames) sel = atomsel(selection="protein", molid=m, frame=1) alasel = atomsel("resname ALA", molid=m) # Minmax without radii assert sel.minmax() == (approx((2.497, 0.724, -0.890)), approx((6.009, 4.623, 2.131))) # Minmax with radii sel.radius = 10.0 assert sel.minmax(radii=True) == (approx((2.497-10., 0.724-10., -0.890-10.)), approx((6.009+10., 4.623+10., 2.131+10.))) assert sel.center(weight=None) == approx((4.040, 2.801, 0.492), rel=1e-3) assert sel.center(weight=[0.]*9+ [1.]) == atomsel("index 9", m).center() # Wrong number of weights with pytest.raises(TypeError): sel.center(weight=3.0) with pytest.raises(ValueError): sel.center([3.,2.]) sel = atomsel("all") assert sel.centerperresidue() == [approx((4.040, 2.801, 0.492), rel=1e-3), approx((7.260, 3.987, 0.000), rel=1e-3)] assert sel.centerperresidue(weight=None)[0] == alasel.centerperresidue()[0] # Centerperresidue with weights with pytest.raises(TypeError): sel.centerperresidue(weight=[True]*len(sel)) with pytest.raises(TypeError): sel.centerperresidue(3) with pytest.raises(ValueError): sel.centerperresidue([1.0,2.0]) weights = [1.0]*10 + [0.0]*6 assert sel.centerperresidue(weight=weights)[0] == approx(alasel.center(), rel=1e-3) assert all([math.isnan(x) for x in sel.centerperresidue(weights)[1]]) molecule.delete(m) # Operations on deleted molecule with pytest.raises(ValueError): sel.minmax(radii=False) with pytest.raises(ValueError): sel.centerrperres() with pytest.raises(ValueError): sel.center()
def _trim_water(self, molid): """ Removes water residues in the +- Z direction in the system. Used to chop off extra waters from the protein that are past a certain cutoff distance. Changes are apparent on next write. Args: molid (int): VMD molecule id to use Returns: int Number of atoms deleted Raises: ValueError if water buffer is None """ if not self.opts.get('wat_buffer'): raise ValueError("Water buffer undefined") # Remove waters in the Z direction # Check if we are trimming to absolute size and set z buf if so total = 0 total = _remove_residues('(not (%s) and not (%s)) and noh and z > %f' % \ (self.solute_sel, self.opts['lipid_sel'], self._zmax), molid=molid) total += _remove_residues( '(not (%s) and not (%s)) and noh and z < %f' % (self.solute_sel, self.opts['lipid_sel'], self._zmin), molid=molid) # Trim in the XY direction if it's a pure water system # lipid trimming is done in trim_xy_residues and takes into account # lipid center, etc. if self.water_only: xcoord = atomsel(self.solute_sel).get('x') buf = (self.size[0] - max(xcoord) + min(xcoord)) / 2. total += _remove_residues('(not (%s)) and noh and x > %f' % (self.solute_sel, max(xcoord) + buf), molid=molid) total += _remove_residues('(not (%s)) and noh and x < %f' % (self.solute_sel, min(xcoord) - buf), molid=molid) ycoord = atomsel(self.solute_sel).get('y') buf = (self.size[1] - max(ycoord) + min(ycoord)) / 2. total += _remove_residues('(not (%s)) and noh and y > %f' % (self.solute_sel, max(ycoord) + buf), molid=molid) total += _remove_residues('(not (%s)) and noh and y < %f' % (self.solute_sel, min(ycoord) - buf), molid=molid) return total
def add_ligands_and_dabble(self): """ Adds remaining ligands to file. Saves all molecules to a temporary file and invokes the Dabble Builder API. Names them according to the replicate id. Args: molid (int): Molecule ID to save """ # Add remaining ligands for _ in range(self.config.getint("system", "num_ligands") - 1): # First already set self._greedily_add_ligand() # Print out resampler info, for easy backtracking print("SAMPLER %d.psf: Clusters: %s" % (self.repindex, self.sampled_clusters)) sys.stdout.flush() # Write temporary file with correct ligand positions filen = os.path.join(self.outdir, "%d_inp.mae" % self.repindex) atomsel("all", self.molid).write('mae', filen) with TemporaryDirectory(dir=os.environ.get("SCRATCH"), prefix="dabble") as tdir: if len(self.dabbleopts.get("topologies", "").strip()): topos = self.dabbleopts["topologies"].split(',') else: topos = None if len(self.dabbleopts.get("parameters", "").strip()): params = self.dabbleopts["parameters"].split(',') else: params = None builder = DabbleBuilder( solute_filename=filen, output_filename=os.path.join(self.outdir, "%d.prmtop" % self.repindex), user_x=float(self.dabbleopts["dimensions"].split(',')[0]), user_y=float(self.dabbleopts["dimensions"].split(',')[1]), user_z=float(self.dabbleopts["dimensions"].split(',')[2]), membrane_system=self.dabbleopts.get("solvent", "DEFAULT"), forcefield=self.dabbleopts["forcefield"], extra_topos=topos, extra_params=params, exclude_sel=self.dabbleopts.get("excludesel"), hmassrepartition=self.config.getboolean("production", "hmr", fallback=True), overwrite=True, tmp_dir=tdir) builder.write()
def check_atom_names(molid): """ Checks that there are no spaces in atom names. If spaces are found, they are removed and a warning is printed """ names = set(atomsel(molid=molid).get('name')) for name in names: if ' ' in name: print("\nWARNING: Found space character in name '%s'\n" " Incompatible with charmm formats, removing it" % name) atomsel("name '%s'", molid=molid).set('name', name.replace(' ', ''))
def test_amber_params(tmpdir): """ Tests writing a ligand with amber parameters """ from Dabble.param import AmberWriter # Parameterize with amber parameters p = str(tmpdir.mkdir("multiligand_rename")) molid = molecule.load("mae", os.path.join(dir, "07HT2B_WT.mae")) w = AmberWriter(molid, tmp_dir=p, forcefield="amber", hmr=False, extra_topos=[os.path.join(dir,"lsd.lib")], extra_params=[os.path.join(dir, "lsd.frcmod")], override_defaults=False) w.write(os.path.join(p, "test")) # Check output is correct m2 = molecule.load("parm7", os.path.join(p, "test.prmtop"), "rst7", os.path.join(p, "test.inpcrd")) molecule.set_top(m2) # Check the system is correctly built assert(len(set(atomsel("protein or resname ACE NMA").get("resid"))) == 298) assert(len(atomsel("water")) == 186) assert(len(atomsel("ion")) == 0) # Check the LSD and CHL (cholesterol) are present assert(len(atomsel("resname LSD")) == 50) assert(len(atomsel("resname CHL")) == 74) # Check the partial charges are set # Here the LSD library has partial charges of 0 except +0.6 on the N2 assert(len(set(atomsel("resname LSD").get("charge"))) == 2) assert(abs(atomsel("resname LSD and name N2").get("charge")[0] - 0.6) < 0.01) assert(abs(set(atomsel("resname LSD and not name N2").get("charge")).pop()) < 0.01)
def _remove_xy_lipids(self, molid): """ Removes residues in the +-XY direction in the system. Used to chop off lipids that are protruding outside of the box dimensions. Args: molid (int): VMD molecule id to use Returns: (int) number of atoms deleted Raises: ValueError if the 'lipid' only contains hydrogens """ # Select residues that are outside the box half_x_size = self.size[0] / 2.0 half_y_size = self.size[1] / 2.0 box_sel_str = 'abs(x) > %f or abs(y) > %f' % (half_x_size, half_y_size) # Identify lipids that have some part outside of the box suspicious_lipid_residues = list(set(atomsel('(%s) and (%s)' % \ (self.opts['lipid_sel'], box_sel_str), molid=molid).get('residue'))) bad_lipids = [] # Delete lipids whose center is too far out of the box, keep others for i in suspicious_lipid_residues: lipid_center = atomsel('noh and residue %s' % str(i), molid=molid).center() # Sanity check if not len(lipid_center): raise ValueError("No heavy atoms found in suspicious residue %s" "Check your input file." % str(i)) if abs(lipid_center[0]) > half_x_size or \ abs(lipid_center[1]) > half_y_size: bad_lipids.append(i) lipid_headgroup_sel = 'residue ' + ' '.join([str(l) for l in bad_lipids]) # Do the deletion removal_sel_str = '(%s) or not (%s)' % (lipid_headgroup_sel, self.opts['lipid_sel']) total = _remove_residues('noh and (%s) and (%s) and not (%s)' % (box_sel_str, removal_sel_str, self.solute_sel), molid=molid) return total
def _convert_water_molecule_to_ion(molid, atom_id, element): """ Converts a water molecule to an ion, deleting the hydgrogens and placing the ion where the water oxygen was Args: molid (int): VMD molecule to operate on atom_id (int): Atom index of water oxygen to change to ion element (str in Na, K, Cl): Ion to apply Raises: ValueError: if invalid element specified ValueError: if atom id is not of an oxygen water """ if element not in ['Na', 'K', 'Cl']: raise ValueError("Ion must be Na, K, or Cl. Was '%s'" % element) element_received = atomsel('index %d' % atom_id).get('element')[0] if element_received is not 'O': raise ValueError("Received non-water oxygen to convert, id %d element %s" % (atom_id, element_received)) molutils.set_ion(molid, atom_id, element) _remove_atoms('element H and same residue as index %d' % atom_id, molid)
def set_ion(molid, atom_id, element): """ Sets an atom to be the desired ion Args: molid (int): VMD molecule to operate on atom_id (int): Atom index to change to ion element (str in Na, K, Cl): Ion to apply Raises: ValueError if the index to change is not present """ sel = atomsel('index %d' % atom_id, molid=molid) if len(sel) == 0: raise ValueError("Index %d does not exist" % atom_id) resname = dict(Na='SOD', K='POT', Cl='CLA')[element] name = dict(Na='NA', K='K', Cl='CL')[element] attype = dict(Na='NA', K='K', Cl='CL')[element] charge = dict(Na=1, K=1, Cl=-1)[element] sel.set('element', element) sel.set('name', name) sel.set('type', attype) sel.set('resname', resname) sel.set('chain', 'N') sel.set('segid', 'ION') sel.set('charge', charge)
def _find_convertible_water_molecule(molid, # pylint: disable=invalid-name water_sel='resname TIP3', min_ion_dist=5.0): """ Finds a water molecule that can be converted to an ion Args: molid (int): VMD molid to look at water_sel (str): VMD atom selection for water min_ion_dist (float): Minimum distance between ionds Returns: (int) Atom index of a water oxygen that is convertible Raises: ValueError if no convertible water molecules are found """ inclusion_sel = 'beta 1 and noh and (%s)' % water_sel exclusion_sel = 'beta 1 and not (%s)' % water_sel sel = atomsel('(%s) and not pbwithin %f of (%s)' \ % (inclusion_sel, min_ion_dist, exclusion_sel), molid) if len(sel) == 0: raise ValueError("No convertible water molecules found in %s" % sel) return sel.get('index')[random.randint(0, len(sel))]
def _check_psf_output(self): """ Scans the output psf from psfgen for atoms where the coordinate could not be set, indicating an unmatched atom. This checek is necessary because sometimes psfgen will run with no errors or warnings but will have unmatched atoms that are all at (0,0,0). """ # Check file was written at all if not os.path.isfile('%s.pdb'% self.psf_name): print("\nERROR: psf file failed to write.\n" " Please see log above.\n") quit(1) # Open the pdb file in VMD and check for atoms with no occupancy fileh = molecule.load('pdb', '%s.pdb' % self.psf_name) errors = atomsel("occupancy=-1", molid=fileh) # Print out error messages if len(errors): print("\nERROR: Couldn't find the following atoms.") for i in range(len(errors)): print(" %s%s:%s" % (errors.get("resname")[i], errors.get("resid")[i], errors.get("name")[i])) print("Check if they are present in the original structure.\n" "If they are, check dabble name translation or file a " "bug report to Robin.\n") quit(1) else: print("\nChecked output pdb/psf has all atoms present " "and correct.\n")
def add_molecule(self, filename, desc): """ Adds a molecule file to the system. Args: filename (str): File to load desc (str): Type of molecule: solute, solvent, etc Returns: True if the molecule was successfully added """ molid = fileutils.load_solute(filename, tmp_dir=self.tmp_dir) self.molids[desc] = molid atomsel('all', molid=molid).set('beta', 1) return True
def test_write(tmpdir, file_3frames): m = molecule.load("pdb", struct_file=file_3frames) tmpdir = str(tmpdir) # Deprecated arguments with pytest.warns(DeprecationWarning): molecule.write(m, "psf", beg=0, end=1, filename=os.path.join(tmpdir, "deprecated.psf")) # Write with stride molecule.write(molid=m, filetype="pdb", first=0, stride=2, filename=os.path.join(tmpdir, "2frames.pdb")) m2 = molecule.load("pdb", os.path.join(tmpdir, "2frames.pdb")) assert molecule.numframes(m2) == 2 # Write a selection sel = atomsel("resname ALA", molid=m) molecule.write(m, "mae", selection=sel, first=0, last=0, filename=os.path.join(tmpdir, "ala.mae")) m3 = molecule.load("mae", os.path.join(tmpdir, "ala.mae")) assert molecule.numframes(m3) == 1 assert set(atomsel(molid=m3).resname) == set(["ALA"]) # Write an invalid selection on a different molid sel2 = atomsel("resname ALA", molid=m3) with pytest.raises(ValueError): molecule.write(m, "mae", os.path.join(tmpdir, "badsel.mae"), selection=sel2) # Write a nonexistent atom selection with pytest.raises(TypeError): molecule.write(m, "mae", os.path.join(tmpdir, "badsel.mae"), selection=None) # Write zero frames with pytest.raises(ValueError): molecule.write(first=20, last=21, molid=m, filetype="psf", filename=os.path.join(tmpdir, "zeroframes.psf")) # Write to an invalid file name (in this case, a directory) with pytest.raises(ValueError): molecule.write(m, "pdb", filename=os.path.join(tmpdir, ".")) molecule.delete(m) molecule.delete(m2) molecule.delete(m3)
def get_bonded_atoms(molid, index): """ Returns the element of all atoms bonded to the current atom. Args: molid (int): VMD molecule ID to consider index (int): Atom index to look at bonded atoms Returns: (list of str) elements of atoms bound to the current atom """ asel = atomsel('index %d' % index, molid=molid) bound = [] for atom in asel.bonds[0]: bound.append(atomsel('index %d' % atom).get('element')[0]) return bound
def center_system(molid, tmp_dir, center_z=False): """ Centers an entire system in the XY-plane, and optionally in the Z dimension. Needs to save and reload the file in case the current positions need to be concatenated to produce a new file. Args: molid (int): VMD molecule id to center tmp_dir (str): Directory to create temp file in center_z (bool): Whether or not to center along the Z axis as well Returns: (int) : VMD molecule id of centered system """ # pylint: disable=invalid-name x, y, z = atomsel('all', molid=molid).center() if center_z is True: atomsel('all', molid=molid).moveby((-x, -y, -z)) else: atomsel('all', molid=molid).moveby((-x, -y, 0)) # Save and reload the solute to record atom positions temp_mae = tempfile.mkstemp(suffix='.mae', prefix='dabble_centered', dir=tmp_dir)[1] atomsel('all', molid=molid).write('mae', temp_mae) molecule.delete(molid) new_id = molecule.load('mae', temp_mae) return new_id
def test_atomsel_sasa(file_3frames): m1 = molecule.load("pdb", file_3frames) m2 = molecule.load("pdb", file_3frames) s1 = atomsel("resname ALA", m1) s2 = atomsel("all", m2) # Invalid radius with pytest.raises(ValueError): s1.sasa(srad=-2.0) assert s1.sasa(srad=0.0) == approx(93.86288452) # Invalid samples with pytest.raises(ValueError): s1.sasa(srad=1.0, samples=0) with pytest.raises(ValueError): s1.sasa(srad=1.0, samples=-10) # Points object invalid with pytest.raises(TypeError): s1.sasa(srad=1.0, points=[]) with pytest.raises(TypeError): s1.sasa(srad=1.0, points=(3,3)) # Test correct calculation assert s1.sasa(srad=1.0) == approx(176.46739) assert s2.sasa(1.0, restrict=s1) == approx(142.44455) assert s1.sasa(0.5) == approx(s1.sasa(0.5, restrict=s1)) # Using points object _, points = s1.sasa(0.5, points=True, samples=1) assert len(points) == 3 # Samples is an upper bound assert points[0] == approx((3.99603, 3.19342, 3.62426), rel=1e-3) # Test on deleted molecule molecule.delete(m1) with pytest.raises(ValueError): s1.sasa(srad=5.0) with pytest.raises(ValueError): s2.sasa(1.0, restrict=s1) molecule.delete(m2)
def test_compare_mol(): from vmd import atomsel, molecule from Dabble.param import CharmmMatcher molid = molecule.load("mae", dir+"lsd_prot.mae") g = CharmmMatcher([dir+"lsd_prot_trunc.str", dir+"masses.rtf"]) (resdict, mdict) = g.get_names(atomsel("all", molid=molid)) assert(mdict=={0: 'C13', 1: 'N20', 2: 'C12', 3: 'C11', 4: 'C15', 5: 'C7', 6: 'C3', 7: 'C8', 8: 'N1', 9: 'C1', 10: 'C4', 11: 'C5', 12: 'C2', 13: 'C6', 14: 'C9', 15: 'C10', 16: 'C14', 17: 'C16', 18: 'O1', 19: 'N3', 20: 'C17', 21: 'C18', 22: 'C19', 23: 'C20', 24: 'H131', 25: 'H132', 26: 'H121', 27: 'H122', 28: 'H123', 29: 'H11', 30: 'H151', 31: 'H8', 32: 'H4', 33: 'H5', 34: 'H2', 35: 'H10', 36: 'H14', 37: 'H171', 38: 'H172', 39: 'H181', 40: 'H182', 41: 'H183', 42: 'H191', 43: 'H192', 44: 'H201', 45: 'H202', 46: 'H203', 47: 'H152', 48: 'HN1', 49: 'HN2'}) assert("LSD"==resdict)
def test_atomsel_selection(file_3nob): m = molecule.load("mae", file_3nob) with pytest.raises(ValueError): atomsel("resname NMA or isala") selection.add_macro(name="isala", selection="resname ALA") s1 = atomsel("resname NMA or isala") assert len(s1) == 46 # TODO: Calling add_macro twice makes 2 entries??? selection.add_macro(name="isala", selection="resname NMA") s1.update() assert len(s1) == 6 selection.del_macro(name="isala") molecule.delete(m)
def test_patches(): from vmd import atomsel, molecule from Dabble.param import CharmmMatcher molid = molecule.load("mae", dir+"phosphoserine.mae") g = CharmmMatcher([dir+"phosphoserine.str"]) (name, patch, mdict) = g.get_patches(atomsel("resname SEP", molid=molid)) assert(name == "SER") assert(patch == "PSEP") assert(mdict=={5: '-C', 16: 'N', 17: 'CA', 18: 'CB', 19: 'OG', 20: 'C', 21: 'O', 22: 'P', 23: 'O1P', 24: 'O2P', 25: 'OT', 26: 'HN', 27: 'HA', 28: 'HB1', 29: 'HB2', 30: '+N'})
def test_evaltcl(file_rho): from vmd import evaltcl, atomsel, molecule molid = int(evaltcl("mol new")) assert molecule.get_top() == molid assert evaltcl("mol addfile %s type mae waitfor all" % file_rho) assert "molecule%d" % molid in evaltcl("mol list") with pytest.raises(ValueError): evaltcl("atomsel all") assert evaltcl("set all_atoms [atomselect %s \" all \" frame %s]" % (molid, 0)) == "atomselect0" assert set(evaltcl("$all_atoms get chain").split()) == \ set(atomsel("all").chain) assert set(evaltcl("$all_atoms get name").split()) == \ set(atomsel("all").name) with pytest.raises(ValueError): evaltcl("$all_atoms get invalid")
def _write_unorderedindex_waters(self, residues, molid): """ Renumbers and sorts the specified waters manually. This is much less efficient but is necessary in cases where atoms within a water molecule are not sequential in index, preventing quick renaming with VMD. Identify problem waters, then call this on them. It'll write its own psf_wat_* file with just those waters, minimizing inefficiency. Args: residues (list of int): Problem water molecules molid (int): VMD molecule ID to write Returns: (int) Number of waters written """ temp = tempfile.mkstemp(suffix='_indexed.pdb', prefix='psf_wat_', dir=self.tmp_dir)[1] fileh = open(temp, 'w') idx = 1 for ridx, residue in enumerate(residues): res = atomsel('residue %d' % residue, molid=molid) for i in res.get('index'): a = atomsel('index %d' % i, molid) # pylint: disable=invalid-name entry = ('%-6s%5d %-5s%-4s%c%4d %8.3f%8.3f%8.3f%6.2f%6.2f' ' %-4s%2s\n' % ('ATOM', idx, a.get('name')[0], a.get('resname')[0], a.get('chain')[0], ridx+1, a.get('x')[0], a.get('y')[0], a.get('z')[0], 0.0, 0.0, a.get('segname')[0], a.get('element')[0])) idx += 1 fileh.write(entry) fileh.write('END\n') fileh.close() return idx
def test_multiligand_parameterizing(tmpdir): """ Checks the parameterization of a system with multiple ligands """ from vmd import atomsel, molecule from Dabble.param import CharmmWriter # Parameterize the multiple ligand system with charmm parameters p = str(tmpdir.mkdir("multiligand_parameterize")) molid = molecule.load("mae", os.path.join(dir, "test_multiligand_correct.mae")) w = CharmmWriter(tmp_dir=p, molid=molid, lipid_sel="lipid", extra_topos=[os.path.join(dir, "alprenolol.rtf")]) w.write(os.path.join(p, "test")) # Load the created file m2 = molecule.load("psf", os.path.join(p, "test.psf")) molecule.set_top(m2) # Check the system is intact assert(len(set(atomsel("protein").get("resid"))) == 282) assert(len(set(atomsel("resname ACE NMA").get("resid"))) == 4) assert(len(atomsel("water")) == 32106) assert(len(atomsel("lipid")) == 12194) # Check for the correct number of alprenolols assert(len(atomsel("resname ALP")) == 420) assert(set(atomsel("resname ALP").get("resid")) == set(range(1,11)))
def test_hmr_param(tmpdir): """ Tests phosphorylations on Ser Also checks HMR """ from Dabble.param import AmberWriter from vmd import atomsel, molecule # Build the system with HMR p = str(tmpdir.mkdir("hmr_param")) molid = molecule.load("mae", os.path.join(dir, "rho_test.mae")) w = AmberWriter(tmp_dir=p, molid=molid, hmr=True, extra_topos=[], extra_params=[]) w.write(os.path.join(p, "test")) # Load the built system for checking # Use psf so we can check HMR was done correctly m2 = molecule.load("psf", os.path.join(p, "test.psf")) m3 = molecule.load("parm7", os.path.join(p, "test.prmtop")) # Check the protein is there with correct capping groups assert len(atomsel("protein", m2)) == 299 assert len(atomsel("protein", m3)) == 299 assert len(set(atomsel("resname ACE NMA", m2).get("resid"))) == 2 assert len(set(atomsel("resname ACE NMA", m3).get("resid"))) == 2 # Check for 3 phosphoserines and no regular serines molecule.set_top(m2) assert atomsel("resname SER").get("name").count("P") == 3 assert not atomsel("resname SER and not same residue as name P") # Check for one phosphothreonine assert len(set(atomsel("resname THR").get("resid"))) == 3 assert len(atomsel("resname THR and name P")) == 1 # Check HMR was done correctly minmasspre = min(atomsel("all", m2).get("mass")) minmasspost = min(atomsel("all", m3).get("mass")) assert abs(minmasspre - 1.008) < 0.0001 assert abs(minmasspost - 1.008) > 0.0001