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_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 test_absolute_box(tmpdir): """ Tests the absolute box size for a system with ligands far from the box """ from vmd import atomsel, molecule from dabble import DabbleBuilder # Build the system p = str(tmpdir) b = DabbleBuilder(solute_filename=os.path.join(dir, "dor_ligs.mae"), output_filename=os.path.join(p, "test.mae"), user_x=75., user_y=75., user_z=115., overwrite=True, tmp_dir=p, exclude_sel="same fragment as resname FRAG") b.write() # Load the built system m2 = molecule.load("mae", os.path.join(p, "test.mae")) molecule.set_top(m2) # Check all the ligands are there assert len(set(atomsel("resname FRAG").residue)) == 3
def check_built_system(p): from vmd import atomsel, molecule # Load the built system for checking assert os.path.isfile(os.path.join(p, "test.prmtop")) # Use psf so we can check HMR was done correctly m3 = molecule.load("parm7", os.path.join(p, "test.prmtop")) # Check the protein is there with correct capping groups assert len(atomsel("protein", m3)) == 299 assert len(set(atomsel("resname ACE NMA NME", m3).get("resid"))) == 2 # Check for 3 phosphoserines and no regular serines molecule.set_top(m3) assert atomsel("resname SER SEP").get("name").count("P") == 3 assert not atomsel("resname SER SEP and not same residue as name P") # Check for one phosphothreonine assert len(set(atomsel("resname THR TPO").get("resid"))) == 3 assert len(atomsel("resname THR TPO and name P")) == 1 # Check HMR was done correctly minmasspost = min(atomsel("all", m3).get("mass")) assert abs(minmasspost - 1.008) > 0.0001
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 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 _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 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 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 test_angles(file_3nob): with pytest.raises(ValueError): topology.angles(molid=0) m = molecule.load("mae", file_3nob) molecule.set_top(m) # No angles defined in a mae file x = topology.angles(molid=m, type=True) print(len(x)) assert len(x) == 0 assert topology.angletypes() == [] # pass invalid molid with pytest.raises(ValueError): topology.addangle(0, 0, 0, molid=-1999) assert topology.addangle(i=0, j=1, k=2, molid=m) == 0 assert topology.angles(m, True) == [(0, 1, 2, None)] x = topology.addangle(0, 0, 0, molid=m, type="angle2") assert topology.angles(m, True)[x] == (0, 0, 0, "angle2") assert topology.delangle(i=0, j=0, k=0, molid=m) assert topology.angles(m, True) == [(0, 1, 2, None)] assert not topology.delangle(1, 2, 3, molid=m) assert topology.delallangles(m) == 1 assert topology.angletypes() == ["angle2"] molecule.delete(m)
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_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_water_box(tmpdir): """ Tests building in a water box only """ from Dabble import DabbleBuilder from Dabble import molutils from vmd import molecule # Build a system with well defined dimensions p = str(tmpdir.mkdir("po4_hmr")) filename = os.path.join(dir, "rho_test.mae") b = DabbleBuilder(solute_filename=filename, output_filename=os.path.join(p, "test.mae"), membrane_system="TIP3", user_x=40.0, user_y=35.0, user_z=65.0, tmp_dir=p) b.write() # Load the built system for checking m2 = molecule.load("mae", os.path.join(p, "test.mae")) molecule.set_top(m2) # Check the system dimensions assert molutils.get_system_dimensions(m2) == (40.0, 35.0, 65.0)
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_covalent_ligand_patches(tmpdir): """ Tests covalently bound ligand parameterization with the graph. One hydrogen atom should be renamed in the palmitoylcysteine. Also tests for detection of phosphorylations on amino acids. """ from vmd import atomsel, molecule from Dabble.param import CharmmWriter # Parameterize with charmm parameters p = str(tmpdir.mkdir("multiligand_rename")) molid = molecule.load("mae", dir + "rho_arr_CYP_prepped_aligned_dowsered.mae") w = CharmmWriter(tmp_dir=p, molid=molid, lipid_sel="lipid", extra_topos=[dir + "CYP_v1.str"]) w.write(os.path.join(p, "test")) # Load the result m2 = molecule.load("psf", os.path.join(p, "test.psf")) molecule.set_top(m2) # Sanity check the system was built completely assert(len(set(atomsel("protein or resname ACE NMA").get("resid"))) == 699) assert(len(set(atomsel("resname ACE NMA").get("resid"))) == 4) assert(len(atomsel("water")) == 654) assert(len(set(atomsel("all").get("fragment"))) == 220) # Check for palmitoylation assert(len(atomsel("resname CYP")) == 116) assert(set(atomsel("resname CYP").get("resid")) == set([322, 323])) # Check for protonation on Asp83 assert(len(atomsel("resid 83 and resname ASP")) == 13) assert(len(atomsel("resid 331 and resname ASP")) == 12) assert("OH1" in atomsel("resid 83 and resname ASP").get("type")) assert("OH1" not in atomsel("resid 331 and resname ASP").get("type")) # Check for protation on Glu134A assert(len(atomsel("resid 134 and resname GLU")) == 16) assert(len(atomsel("resid 247 and resname GLU")) == 15) assert("OB" in atomsel("resid 134 and resname GLU").get("type")) assert("OB" not in atomsel("resid 331 and resname GLU").get("type")) # Check that Ser98 is normal assert(len(atomsel("resid 98 and resname SER")) == 11) assert("P" not in atomsel("resid 98 and resname SER").get("type")) assert("OH1" in atomsel("resid 98 and resname SER").get("type")) # Check for phosphorylation on Ser334 assert(len(atomsel("resid 334 and resname SER")) == 14) assert("P" in atomsel("resid 334 and resname SER").get("type")) assert("OH1" not in atomsel("resid 334 and resname SER").get("type")) # Check for phosphorylation on Ser338 assert(len(atomsel("resid 338 and resname SER")) == 14) assert("P" in atomsel("resid 338 and resname SER").get("type")) assert("OH1" not in atomsel("resid 338 and resname SER").get("type"))
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) # Get ion resids that aren't associated w other molecules # because some ligands have Na, Cl, K total = atomsel('element Na Cl K') if len(total): not_ions = atomsel("(same fragment as element Na Cl K) and (not index %s)" % " ".join([str(s) for s in set(total.get('index'))])) ions = set(total.get('residue')) - set(not_ions.get('residue')) else: ions = set(total.get('residue')) if not len(ions): return ionstr = "residue " + " ".join([str(s) for s in ions]) # Fix the names atomsel('%s and name NA' % ionstr).set('name', 'SOD') atomsel('%s and name CL' % ionstr).set('name', 'CLA') atomsel('%s and name K' % ionstr).set('name', 'POT') atomsel('%s and name NA' % ionstr).set('resname', 'SOD') atomsel('%s and name CL' % ionstr).set('resname', 'CLA') atomsel('%s and name K' % ionstr).set('resname', 'POT') # Renumber the residues since some may be above 10k residues = atomsel('name SOD CLA POT').get('residue') batch = atomsel('residue %s' % ' '.join([str(s) for s in set(residues)])) batch.set('resid', [k for k in range(1, len(batch)+1)]) # Save the temporary ions file temp = tempfile.mkstemp(suffix='.pdb', prefix='psf_ions_', dir=self.tmp_dir)[1] atomsel('name SOD CLA POT').set('user', 0.0) # mark as saved atomsel('name SOD CLA POT').write('pdb', temp) string = ''' set ionfile %s segment I { pdb $ionfile first none last none } coordpdb $ionfile I ''' % temp self.file.write(string) molecule.set_top(old_top)
def _set_cell_to_square_prism(self, molid): """ Sets the periodic box to be a square prism of specified dimension Args: molid (int): VMD molecule ID to consider """ old_top = molecule.get_top() molecule.set_top(molid) molecule.set_periodic(-1, -1, self.size[0], self.size[1], self.size[2], 90.0, 90.0, 90.0) molecule.set_top(old_top)
def _write_ordered_pdb(filename, sel, molid): """ Writes a pdb file in order of residues, renumbering the atoms accordingly, since psfgen wants each residue sequentially while VMD will write them in the same order as input, which from Maestro created files has some guessed atoms at the end. Args: filename (str): Name of the pdb file to write sel (str): VMD atomsel string for atoms that will be written molid (int): VMD molecule ID to write from """ old_top = molecule.get_top() molecule.set_top(molid) fileh = open(filename, 'w') # Use resids since order can be wrong when sorting by residue # Then, use residue to pull out each one since it is much much # faster then trying to pull out residues resids = set(atomsel(sel).get('resid')) # Add additional residue constraint to selection since pulling out # by resid can match something in a different chain resstr = ' '.join([str(x) for x in set(atomsel(sel).get('residue'))]) idx = 1 # For renumbering capping groups for resid in sorted(resids): # Check for alternate locations residues = sorted(set(atomsel("resid '%s' and residue %s" % (resid, resstr)).get('residue'))) for rid in residues: for i in atomsel('residue %d' % rid).get('index'): a = atomsel('index %d' % i) # pylint: disable=invalid-name ins = a.get("insertion")[0] entry = ('%-6s%5d %-5s%-4s%c%4d%c %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], a.get('resid')[0], ins if len(ins) else " ", 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') atomsel(sel).set('user', 0.0) # Mark as written fileh.close() molecule.set_top(old_top)
def _write_lipid_blocks(self): """ Writes a temporary PDB file containing the lipids for later use by psfgen. Renumbers the lipid residues because some can have **** instead of an integer for resid in large systems, which will crash psfgen. Also sets atom names for some common lipids (currently POPC) Raises: NotImplementedError if more than 10,000 lipids are present since it doesn't support feeding multiple lipid blocks to psfgen currently NotImplementedError if lipid other than POPC,POPE,POPG is found """ # Put current molecule on top to simplify atom selection old_top = molecule.get_top() molecule.set_top(self.molid) # Collect lipid residues up alll = atomsel('(%s) and user 1.0' % self.lipid_sel) residues = list(set(alll.residue)) # Lipids not compatible with AMBER parameters, CHARMM format if alll and ("amber" in self.forcefield or "opls" in self.forcefield): raise ValueError( "AMBER or OPLS parameters not supported for lipids" " in CHARMM output format") # Sanity check for < 10k lipids if len(residues) >= 10000: raise NotImplementedError("More than 10k lipids found") # Loop through all residues and renumber and correctly name them lipress = [] for resname in set(alll.resname): lipress.extend(self._rename_by_resname(resname, renumber=True)) # Write temporary lipid pdb _, temp = tempfile.mkstemp(suffix='.pdb', prefix='psf_lipid_', dir=self.tmp_dir) os.close(_) saved_lips = atomsel("residue %s" % ' '.join(str(_) for _ in lipress)) saved_lips.user = 0.0 saved_lips.write('pdb', temp) # Generate lipid segment self.psfgen.add_segment(segid="L", pdbfile=temp) self.psfgen.read_coords(segid="L", filename=temp) # Put old top back molecule.set_top(old_top)
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) # Get ion resids that aren't associated w other molecules # because some ligands have Na, Cl, K total = atomsel('element Na Cl K') not_ions = atomsel("(same fragment as element Na Cl K) and (not index %s)" % " ".join([str(s) for s in set(total.get('index'))])) ions = set(total.get('residue')) - set(not_ions.get('residue')) if not len(ions): return ionstr = "residue " + " ".join([str(s) for s in ions]) # Fix the names atomsel('%s and name NA' % ionstr).set('name', 'SOD') atomsel('%s and name CL' % ionstr).set('name', 'CLA') atomsel('%s and name K' % ionstr).set('name', 'POT') atomsel('%s and name NA' % ionstr).set('resname', 'SOD') atomsel('%s and name CL' % ionstr).set('resname', 'CLA') atomsel('%s and name K' % ionstr).set('resname', 'POT') # Renumber the residues since some may be above 10k residues = atomsel('name SOD CLA POT').get('residue') batch = atomsel('residue %s' % ' '.join([str(s) for s in set(residues)])) batch.set('resid', [k for k in range(1, len(batch)+1)]) # Save the temporary ions file temp = tempfile.mkstemp(suffix='.pdb', prefix='psf_ions_', dir=self.tmp_dir)[1] atomsel('name SOD CLA POT').set('user', 0.0) # mark as saved atomsel('name SOD CLA POT').write('pdb', temp) string = ''' set ionfile %s segment I { pdb $ionfile first none last none } coordpdb $ionfile I ''' % temp self.file.write(string) molecule.set_top(old_top)
def test_mol_attrs(file_3nob): m1 = molecule.load("mae", file_3nob) m2 = molecule.load("mae", file_3nob) # Get/set top assert molecule.get_top() == m2 molecule.set_top(molid=m1) assert molecule.get_top() == m1 with pytest.raises(ValueError): molecule.set_top(m2+1) # Get/set visibility molecule.set_visible(m1, visible=False) assert molecule.get_visible() == False assert molecule.get_visible(molid=m2) == True with pytest.warns(DeprecationWarning): molecule.set_visible(m1, state=True) assert molecule.get_visible(molid=m1) == True with pytest.raises(ValueError): molecule.set_visible(m2+1, True) with pytest.raises(TypeError): molecule.set_visible(m2, 3) with pytest.raises(ValueError): molecule.get_visible(m2+1) # Get/set periodic assert molecule.get_periodic(m2) == {'a': 1.0, 'alpha': 90.0, 'b': 1.0, 'beta': 90.0, 'c': 1.0, 'gamma': 90.0} with pytest.raises(ValueError): molecule.get_periodic(molid=m1, frame=3000) with pytest.raises(ValueError): molecule.set_periodic(molid=m2+1, a=2.0) with pytest.raises(ValueError): molecule.set_periodic(m1, frame=3000, a=20.0) molecule.set_periodic(m2, frame=0, a=90.0, b=90.0, c=90.0, alpha=90.0, beta=90.0, gamma=90.0) assert list(molecule.get_periodic(m2, frame=0).values()) == [pytest.approx(90.0)]*6 assert set(molecule.get_periodic(m1, frame=0).values()) != [pytest.approx(90.0)]*6 molecule.set_periodic(c=20.0) assert molecule.get_periodic()["c"] == pytest.approx(20.0) molecule.delete(m1) molecule.delete(m2)
def _write_ordered_pdb(filename, sel, molid): """ Writes a pdb file in order of residues, renumbering the atoms accordingly, since psfgen wants each residue sequentially while VMD will write them in the same order as input, which from Maestro created files has some guessed atoms at the end. Args: filename (str): Name of the pdb file to write sel (str): VMD atomsel string for atoms that will be written molid (int): VMD molecule ID to write from """ old_top = molecule.get_top() molecule.set_top(molid) fileh = open(filename, 'w') # Use resids since order can be wrong when sorting by residue # Then, use residue to pull out each one since it is much much # faster then trying to pull out residues resids = set(atomsel(sel).get('resid')) # Add additional residue constraint to selection since pulling out # by resid can match something in a different chain resstr = ' '.join([str(x) for x in set(atomsel(sel).get('residue'))]) idx = 1 # For renumbering capping groups for resid in sorted(resids): rid = atomsel("resid '%s' and residue %s" % (resid, resstr)).get('residue')[0] for i in atomsel('residue %d' % rid).get('index'): a = atomsel('index %d' % i) # 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], a.get('resid')[0], 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') atomsel(sel).set('user', 0.0) # Mark as written fileh.close() molecule.set_top(old_top)
def write(self, filename): """ Writes the parameter and topology files Args: filename (str): File name to write. File type suffix will be added. """ self.outprefix = filename # Put our molecule on top old_top = molecule.get_top() molecule.set_top(self.molid) # Amber forcefield done with AmberWriter then conversion if "amber" in self.forcefield: # Avoid circular import by doing it here from dabble.param import AmberWriter prmtopgen = AmberWriter(molid=self.molid, tmp_dir=self.tmp_dir, forcefield=self.forcefield, water_model=self.water_model, hmr=self.hmr, lipid_sel=self.lipid_sel, extra_topos=self.extra_topos, extra_params=self.extra_params, override_defaults=self.override, debug_verbose=self.debug) prmtopgen.write(self.outprefix) self._prmtop_to_charmm() # Charmm forcefield elif "charmm" in self.forcefield: self._run_psfgen() # OPLS forcefield. Same as charmm but list separately for readability elif "opls" in self.forcefield: self._run_psfgen() else: raise DabbleError("Unsupported forcefield '%s' for CharmmWriter" % self.forcefield) # Check output and finish up self._check_psf_output() # Reset top molecule molecule.set_top(old_top)
def test_covalent_ligand_gromacs_charmm(tmpdir): from vmd import atomsel, molecule from dabble.param import GromacsWriter # Parameterize with charmm parameters p = str(tmpdir) molid = molecule.load( "mae", os.path.join(dir, "rho_arr_CYP_prepped_aligned_dowsered.mae")) w = GromacsWriter(tmp_dir=p, molid=molid, forcefield="charmm", extra_topos=[os.path.join(dir, "CYP_v1.str")]) w.write(os.path.join(p, "test")) # Load the result m2 = molecule.load("gro", os.path.join(p, "test.gro")) molecule.set_top(m2) # Sanity check the system was built completely assert len(set(atomsel("protein or resname ACE NMA").residue)) == 697 assert len(atomsel("name N C O CA")) == 2784 assert len(set(atomsel("resname ACE NMA").resid)) == 4 assert len(atomsel("water")) == 654 # Check for palmitoylation assert len(atomsel("resname CYP")) == 116 assert set(atomsel("resname CYP").resid) == set([322, 323]) # Check for protonation on Asp83 assert len(atomsel("resid 83 and resname ASP")) == 13 assert len(atomsel("resid 331 and resname ASP")) == 12 # Check for protonation on Glu134A assert len(atomsel("resid 134 and resname GLU")) == 16 assert len(atomsel("resid 247 and resname GLU")) == 15 # Check that Ser98 is normal assert len(atomsel("resid 98 and resname SER")) == 11 # Check for phosphorylation on Ser334 assert len(atomsel("resid 334 and resname SER")) == 14 # Check for phosphorylation on Ser338 assert len(atomsel("resid 338 and resname SER")) == 14
def test_amber_custom_residues(tmpdir): from Dabble.param import AmberWriter # Generate the file p = str(tmpdir.mkdir("3nob_custom")) molid = molecule.load("mae", os.path.join(dir, "prepped.mae")) w = AmberWriter(molid, tmp_dir=p, forcefield="amber", hmr=False, extra_topos=[ os.path.join(dir, "glx.off"), os.path.join(dir, "lyx.off") ], extra_params=[ os.path.join(dir, "join.frcmod"), os.path.join(dir, "analogies.frcmod") ], override_defaults=False) w.write(os.path.join(p, "test")) # Load the output file and start checking it m2 = molecule.load("parm7", os.path.join(p, "test.prmtop"), "rst7", os.path.join(p, "test.inpcrd")) molecule.set_top(m2) # Check the two custom residues are present assert (len(atomsel("resname GLX")) == 7) assert (len(atomsel("resname LYX")) == 20) # Check the custom residues have gaff2 atom types assert ("n" in atomsel("resname LYX").get("type")) assert ("n2" in atomsel("resname GLX").get("type")) # Check the normal residues have ff14SB atom types assert ("N" in atomsel("resname LYS").get("type")) assert ("N" in atomsel("resname GLY").get("type")) # Check that the isopeptide bond is there lybonds = [] for x in atomsel("resname LYX").bonds: lybonds.extend(x) assert (any(x in lybonds for x in atomsel("resname GLX").get("index")))
def test_multiligand_amber(tmpdir): """ Checks that multiple ligands work with AMBER. Should rename some atoms in matching """ from vmd import atomsel, molecule from Dabble.param import AmberWriter # Parameterize the system. One atom name will be changed. p = str(tmpdir.mkdir("multiligand_amber")) molid = molecule.load("mae", os.path.join(dir, "test_multiligand_correct.mae")) w = AmberWriter(tmp_dir=p, molid=molid, forcefield="amber", extra_topos=[os.path.join(dir, "alp.off")], extra_params=[os.path.join(dir, "alp.frcmod")]) w.write(os.path.join(p, "test")) # Load the built system and see if it works m2 = molecule.load("parm7", os.path.join(p, "test.prmtop"), "rst7", os.path.join(p, "test.inpcrd")) molecule.set_top(m2) # Check results assert len(set(atomsel("protein").get("resid"))) == 282 assert len(set(atomsel("resname ACE NME").get("resid"))) == 4 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").get("resid"))) == 10 assert "OX" in set(atomsel("resname ALP").get("name")) assert "O1" not in set(atomsel("resname ALP").get("name")) # Check coordinates are unique (not zero) # It's 419 because two of them have the same x assert len(atomsel("resname ALP")) == 420 assert len(set(atomsel("resname ALP").get("x"))) == 419 molecule.delete(m2)
def _write_ordered_pdb(filename, sel, molid): """ Writes a pdb file in order of residues, renumbering the atoms accordingly, since psfgen wants each residue sequentially while VMD will write them in the same order as input, which from Maestro created files has some guessed atoms at the end. Args: filename (str): Name of the pdb file to write sel (str): VMD atomsel string for atoms that will be written molid (int): VMD molecule ID to write from """ old_top = molecule.get_top() molecule.set_top(molid) fileh = open(filename, 'w') # Use resids since order can be wrong when sorting by residue # Then, use residue to pull out each one since it is much much # faster then trying to pull out residues resids = set(atomsel(sel).resid) # Add additional residue constraint to selection since pulling out # by resid can match something in a different chain resstr = ' '.join([str(x) for x in set(atomsel(sel).residue)]) idx = 1 # For renumbering capping groups for resid in sorted(resids): # Check for alternate locations residues = sorted( set( atomsel("resid '%s' and residue %s" % (resid, resstr)).residue)) for rid in residues: for i in atomsel('residue %d' % rid).index: a = atomsel('index %d' % i) # pylint: disable=invalid-name fileh.write(MoleculeWriter.get_pdb_line(a, idx, a.resid[0])) idx += 1 fileh.write('END\n') atomsel(sel).user = 0.0 fileh.close() molecule.set_top(old_top)
def check_correctness(molid): """ Verifies molecule is sane """ molecule.set_top(molid) # Check the protein is there with the correct capping groups assert len(atomsel("protein")) == 804 assert len(set(atomsel("all").get("fragment"))) == 2 assert len(set(atomsel("resname ACE NMA NME").get("residue"))) == 4 # Check for 6 cysteines, 2 with same resid assert len(set(atomsel("resname CYS CYX").get("residue"))) == 6 # Check connectivity between cysteines is correct for res in set(atomsel("resname CYS CYX").get("residue")): assert len(atomsel("residue %d" % res)) == 10 assert len(atomsel("residue %d and name SG" % res)) == 1 idxs = atomsel("residue %d and name SG" % res).bonds[0] assert set(atomsel("index %s" % " ".join(str(i) for i in idxs)).get("name")) \ == set(["CB", "SG"])
def check_correctness(molid): """ Verifies molecule is sane """ molecule.set_top(molid) # Check the protein is there with the correct capping groups assert len(atomsel("protein or resname ACE NMA NME")) == 828 assert len(set(atomsel("all").fragment)) == 2 assert len(set(atomsel("resname ACE NMA NME").residue)) == 4 # Check for 6 cysteines, 2 with same resid assert len(set(atomsel("resname CYS CYX").residue)) == 6 # Check connectivity between cysteines is correct for res in set(atomsel("resname CYS CYX").residue): assert len(atomsel("residue %d" % res)) == 10 assert len(atomsel("residue %d and name SG" % res)) == 1 idxs = atomsel("residue %d and name SG" % res).bonds[0] assert set(atomsel("index %s" % " ".join(str(i) for i in idxs)).name) \ == set(["CB", "SG"])
def verify_amber(molid): molecule.set_top(molid) # Check the two custom residues are present assert len(atomsel("resname GLX")) == 7 assert len(atomsel("resname LYX")) == 20 # Check the custom residues have gaff2 atom types assert "n" in atomsel("resname LYX").type assert "n2" in atomsel("resname GLX").type # Check the normal residues have ff14SB atom types assert "N" in atomsel("resname LYS").type assert "N" in atomsel("resname GLY").type # Check that the isopeptide bond is there lybonds = [] for x in atomsel("resname LYX").bonds: lybonds.extend(x) assert any(x in lybonds for x in atomsel("resname GLX").index)
def test_gromacs_amber(tmpdir): from dabble.param import GromacsWriter p = str(tmpdir) molid = molecule.load("mae", os.path.join(dir, "prepped.mae")) w = GromacsWriter(molid, tmp_dir=p, forcefield="amber", extra_topos=[ os.path.join(dir, "glx.off"), os.path.join(dir, "lyx.off") ], extra_params=[ os.path.join(dir, "join.frcmod"), os.path.join(dir, "analogies.frcmod") ], override_defaults=False) w.write(os.path.join(p, "test")) # Load and check the output file m2 = molecule.load("gro", os.path.join(p, "test.gro")) molecule.set_top(m2) # Check the two custom residues are present assert len(atomsel("resname GLX")) == 7 assert len(atomsel("resname LYX")) == 20 # Check that the isopeptide bond is there lybonds = [] for x in atomsel("resname LYX").bonds: lybonds.extend(x) assert any(x in lybonds for x in atomsel("resname GLX").index) # Check the parameterized file with parmed API from parmed.formats.registry import load_file f = load_file(os.path.join(p, "test.top")) assert f.residues[153].name == "GLX" assert "n2" in [_.type for _ in f.residues[153].atoms] assert "n2" not in [_.type for _ in f.residues[152].atoms] assert "N" in [_.type for _ in f.residues[152].atoms]
def test_multiligand_chamber(tmpdir): """ Checks that multiple ligands work with AMBER. Should rename some atoms in matching """ from vmd import atomsel, molecule from Dabble.param import AmberWriter # Parameterize the system. One atom name will be changed. p = str(tmpdir.mkdir("multiligand_amber")) molid = molecule.load("mae", os.path.join(dir, "test_multiligand_correct.mae")) w = AmberWriter(tmp_dir=p, molid=molid, forcefield="charmm", extra_topos=[os.path.join(dir, "alprenolol.rtf")], extra_params=[os.path.join(dir, "alprenolol.prm")]) w.write(os.path.join(p, "test")) # Load result m2 = molecule.load("psf", os.path.join(p, "test.psf"), "pdb", os.path.join(p, "test.pdb")) molecule.set_top(m2) # Check results 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("same fragment as lipid")) == 12194 # Check for the corrrect number of alprenolols assert len(atomsel("resname ALP")) == 420 assert len(set(atomsel("resname ALP").get("resid"))) == 10 assert "O1" in set(atomsel("resname ALP").get("name")) assert "OX" not in set(atomsel("resname ALP").get("name")) # Check coordinates are unique (not zero) # Two atoms have the same X but that is okay assert len(set(atomsel("resname ALP").get("x"))) == 419 molecule.delete(m2)
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
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"), "pdb", os.path.join(p, "test.pdb")) 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)) # Check coordinates are unique (not zero) # It's 419 because two of them have the same x assert len(atomsel("resname ALP")) == 420 assert len(set(atomsel("resname ALP").get("x"))) == 419 molecule.delete(m2)
def combine_molecules(input_ids, tmp_dir): """ Combines input molecules, closes them and returns the molecule id of the new molecule that combines them, putting it on top. Args: input_ids (list of int): Molecule IDs to combine, will be closed tmp_dir (str): Directory to put combined molecule into Returns: (int) molid of combined system """ output_filename = tempfile.mkstemp(suffix='.mae', prefix='dabble_combine', dir=tmp_dir)[1] concatenate_mae_files(output_filename, input_ids=input_ids) output_id = molecule.load('mae', output_filename) molecule.set_top(output_id) for i in input_ids: molecule.delete(i) atomsel('all', molid=output_id).beta = 1 return output_id
def combine_molecules(input_ids, tmp_dir): """ Combines input molecules, closes them and returns the molecule id of the new molecule that combines them, putting it on top. Args: input_ids (list of int): Molecule IDs to combine, will be closed tmp_dir (str): Directory to put combined molecule into Returns: (int) molid of combined system """ output_filename = tempfile.mkstemp(suffix='.mae', prefix='dabble_combine', dir=tmp_dir)[1] fileutils.concatenate_mae_files(output_filename, input_ids=input_ids) output_id = molecule.load('mae', output_filename) molecule.set_top(output_id) for i in input_ids: molecule.delete(i) atomsel('all', molid=output_id).set('beta', 1) return output_id
def test_amber_custom_residues(tmpdir): from Dabble.param import AmberWriter # Generate the file p = str(tmpdir.mkdir("3nob_custom")) molid = molecule.load("mae", os.path.join(dir, "prepped.mae")) w = AmberWriter(molid, tmp_dir=p, forcefield="amber", hmr=False, extra_topos=[os.path.join(dir, "glx.off"), os.path.join(dir, "lyx.off")], extra_params=[os.path.join(dir, "join.frcmod"), os.path.join(dir, "analogies.frcmod")], override_defaults=False) w.write(os.path.join(p, "test")) # Load the output file and start checking it m2 = molecule.load("parm7", os.path.join(p, "test.prmtop"), "rst7", os.path.join(p, "test.inpcrd")) molecule.set_top(m2) # Check the two custom residues are present assert(len(atomsel("resname GLX")) == 7) assert(len(atomsel("resname LYX")) == 20) # Check the custom residues have gaff2 atom types assert("n" in atomsel("resname LYX").get("type")) assert("n2" in atomsel("resname GLX").get("type")) # Check the normal residues have ff14SB atom types assert("N" in atomsel("resname LYS").get("type")) assert("N" in atomsel("resname GLY").get("type")) # Check that the isopeptide bond is there lybonds = [] for x in atomsel("resname LYX").bonds: lybonds.extend(x) assert(any(x in lybonds for x in atomsel("resname GLX").get("index")))
def _write_generic_block(self, residues): """ Matches ligands to available topology file, renames atoms, and then writes temporary files for the ligands Args: residues (list of int): Residue numbers to be written. Will all be written to one segment. Returns: True if successful """ # Put our molecule on top to simplify atom selection language old_top = molecule.get_top() molecule.set_top(self.molid) alig = atomsel('user 1.0 and residue %s' % " ".join([str(x) for x in residues])) # Write temporary file containg the residues and update tcl commands temp = tempfile.mkstemp(suffix='.pdb', prefix='psf_block_', dir=self.tmp_dir)[1] string = ''' set blockfile %s segment B%s { pdb $blockfile first none last none } coordpdb $blockfile B%s ''' % (temp, residues[0], residues[0]) alig.write('pdb', temp) alig.set('user', 0.0) self.file.write(string) if old_top != -1: molecule.set_top(old_top) return True
def test_water_box(tmpdir): """ Tests building in a water box only """ from Dabble import DabbleBuilder from Dabble import molutils from vmd import molecule # Build a system with well defined dimensions p = str(tmpdir.mkdir("po4_hmr")) filename = os.path.join(dir, "rho_test.mae") b = DabbleBuilder(solute_filename=filename, output_filename=os.path.join(p, "test.mae"), membrane_system="TIP3", user_x=40.0, user_y=35.0, user_z = 65.0, tmp_dir=p) b.write() # Load the built system for checking m2 = molecule.load("mae", os.path.join(p, "test.mae")) molecule.set_top(m2) # Check the system dimensions assert molutils.get_system_dimensions(m2) == (40.0, 35.0, 65.0)
def _write_generic_block(self, residues): """ Matches ligands to available topology file, renames atoms, and then writes temporary files for the ligands Args: residues (list of int): Residue numbers to be written. Will all be written to one segment. Returns: True if successful """ # Put our molecule on top to simplify atom selection language old_top = molecule.get_top() molecule.set_top(self.molid) alig = atomsel('user 1.0 and residue %s' % " ".join([str(x) for x in residues])) # Write temporary file containg the residues and update tcl commands _, temp = tempfile.mkstemp(suffix='.pdb', prefix='psf_block_', dir=self.tmp_dir) os.close(_) alig.write('pdb', temp) alig.user = 0.0 # Get next available segment name segname = "B%d" % self.segint self.segint += 1 self.psfgen.add_segment(segid=segname, pdbfile=temp) self.psfgen.read_coords(segid=segname, filename=temp) if old_top != -1: molecule.set_top(old_top) return True
def _find_single_residue_names(self, resname, molid): """ Uses graph matcher and available topologies to match up ligand names automatically. Tries to use graphs, and if there's an uneven number of atoms tries to match manually to suggest which atoms are most likely missing. Args: resname (str): Residue name of the ligand that will be written. All ligands will be checked separately against the graphs. molid (int): VMD molecule ID to consider Returns: (list of ints): Residue numbers (not resid) of all input ligands that were successfully matched. Need to do it this way since residue names can be changed in here to different things. Raises: ValueError if number of resids does not match number of residues as interpreted by VMD NotImplementedError if a residue could not be matched to a graph. """ # Put our molecule on top old_top = molecule.get_top() molecule.set_top(molid) # Sanity check that there is no discrepancy between defined resids and # residues as interpreted by VMD. for chain in set(atomsel("user 1.0 and resname '%s'" % resname).get('chain')): residues = list(set(atomsel("user 1.0 and resname '%s' and chain %s" % (resname, chain)).get('residue'))) resids = list(set(atomsel("user 1.0 and resname '%s' and chain %s" % (resname, chain)).get('resid'))) if len(residues) != len(resids): raise ValueError("VMD found %d residues for resname '%s', but there " "are %d resids! Check input." % (len(residues), resname, len(resids))) for residue in residues: sel = atomsel("residue %s and resname '%s' and user 1.0" % (residue, resname)) (newname, atomnames) = self.matcher.get_names(sel, print_warning=True) if not newname: (resname, patch, atomnames) = self.matcher.get_patches(sel) if not newname: print("ERROR: Could not find a residue definition for %s:%s" % (resname, residue)) raise NotImplementedError("No residue definition for %s:%s" % (resname, residue)) print("\tApplying patch %s to ligand %s" % (patch, newname)) # Do the renaming for idx, name in atomnames.items(): atom = atomsel('index %s' % idx) if atom.get('name')[0] != name and "+" not in name and \ "-" not in name: print("Renaming %s:%s: %s -> %s" % (resname, residue, atom.get('name')[0], name)) atom.set('name', name) sel.set('resname', newname) #logger.info("Renamed %d atoms for all resname %s->%s" % (num_renamed, resname, name)) molecule.set_top(old_top) return residues
def test_bonds(file_3nob): with pytest.raises(ValueError): topology.bonds(molid=0) m = molecule.load("mae", file_3nob) molecule.set_top(m) with pytest.raises(ValueError): topology.bonds(molid=m, type=3, order=True) with pytest.warns(DeprecationWarning): x = topology.bonds(molid=m, type=3) assert len(x) == 2493 assert x[0] == (0, 1, None, 1.0) with pytest.warns(DeprecationWarning): x = topology.bonds(molid=m, type=2) assert len(x) == 2493 assert x[0] == (0, 1, 1.0) with pytest.warns(DeprecationWarning): x = topology.bonds(molid=m, type=1) assert len(x) == 2493 assert x[0] == (0, 1, None) x = topology.bonds(molid=m, type=True) assert len(x) == 2493 assert x[0] == (0, 1, None) x = topology.bonds(molid=m, type=False, order=True) assert len(x) == 2493 assert x[0] == (0, 1, 1.0) x = topology.bonds(molid=m, type=True, order=True) assert len(x) == 2493 assert x[0] == (0, 1, None, 1.0) x = topology.bonds(molid=m) assert len(x) == 2493 assert len(x[0]) == 2 assert (0, 1) in x # Test adding a bond assert (0, 2) not in x topology.addbond(i=0, j=2, molid=-1) assert (0, 2, None, 1.0) in topology.bonds(molid=m, order=True, type=True) assert (2, 3) not in x assert topology.bondtypes(m) == [] topology.addbond(i=2, j=3, molid=m, order=2.0, type="test") assert (2, 3, "test", 2.0) in topology.bonds(molid=m, order=True, type=True) assert topology.bondtypes(m) == ["test"] # Test deleting a bond assert topology.delbond(i=2, j=0, molid=m) assert (0, 2) not in topology.bonds(molid=m) assert not topology.delbond(0, 0, molid=m) assert topology.delbond(i=2, j=3) assert topology.bondtypes(m) == ["test"] # bond type not deleted with pytest.raises(ValueError): topology.delbond(i=-1, j=5, molid=m) with pytest.raises(ValueError): topology.delbond(i=0, j=1, molid=-5) with pytest.raises(ValueError): topology.delallbonds(molid=2389182) assert topology.delallbonds(m) == 2493 assert len(topology.bonds(m)) == 0 assert topology.delallbonds(m) == 0 molecule.delete(m)
def _write_protein_blocks(self, molid, frag): """ Writes a protein fragment to a pdb file for input to psfgen Automatically assigns amino acid names Args: molid (int): VMD molecule ID of renumbered protein frag (str): Fragment to write Returns: (list of str): Patches to add to the psfgen input file after all proteins have been loaded """ print("Setting protein atom names") # Put our molecule on top to simplify atom selection language old_top = molecule.get_top() molecule.set_top(molid) patches = set() extpatches = set() seg = "P%s" % frag residues = list(set(atomsel("fragment '%s'" % frag).get('residue'))) for residue in residues: sel = atomsel('residue %s' % residue) resid = sel.get('resid')[0] # Only try to match single amino acid if there are 1 or 2 bonds if len(self.matcher.get_extraresidue_atoms(sel)) < 3: (newname, atomnames) = self.matcher.get_names(sel, print_warning=False) # See if it's a disulfide bond participant else: (newname, patchline, atomnames) = \ self.matcher.get_disulfide("residue %d" % residue, frag, molid) if newname: extpatches.add(patchline) # Couldn't find a match. See if it's a patched residue if not newname: (newname, patch, atomnames) = self.matcher.get_patches(sel) if newname: patches.add("patch %s %s:%d\n" % (patch, seg, resid)) # Fall through to error condition if not newname: raise ValueError("Couldn't find a patch for %s:%s" % (sel.get('resname')[0], resid)) # Do the renaming for idx, name in atomnames.items(): atom = atomsel('index %s' % idx) if atom.get('name')[0] != name and "+" not in name and \ "-" not in name: atom.set('name', name) sel.set('resname', newname) # Save protein chain in the correct order filename = self.tmp_dir + '/psf_protein_%s.pdb' % seg _write_ordered_pdb(filename, "fragment '%s'" % frag, molid) print("\tWrote %d atoms to the protein segment %s" % (len(atomsel("fragment %s" % frag)), seg)) # Now write to psfgen input file string = ''' set protnam %s segment %s { first none last none pdb $protnam } ''' % (filename, seg) self.file.write(string) print("Applying the following single-residue patches to P%s:\n" % frag) print("\t%s" % "\t".join(patches)) self.file.write(''.join(patches)) self.file.write("\n") self.file.write("coordpdb $protnam %s\n" % seg) if old_top != -1: molecule.set_top(old_top) return extpatches
def _renumber_protein_chains(self, molid): """ Pulls all protein fragments and renumbers the residues so that ACE and NMA caps appear to be different residues to VMD. This is necessary so that they don't appear as patches. Proteins with non standard capping groups will have the patches applied. Args: molid (int): VMD molecule ID of entire system Returns: (int): Molid of loaded fragment """ # Put our molecule on top and grab selection old_top = molecule.get_top() molecule.set_top(molid) for frag in set(atomsel("protein or resname ACE NMA").get("fragment")): fragment = atomsel('fragment %s' % frag, molid=molid) print("Checking capping groups resids on protein fragment %d" % frag) for resid in sorted(set(fragment.get("resid"))): # Handle bug where capping groups in same residue as the # neighboring amino acid Maestro writes it this way for some # reason but it causes problems down the line when psfgen doesn't # understand the weird combined residue rid = atomsel("fragment '%s' and resid '%d'" % (frag, resid)).get('residue')[0] names = set(atomsel('residue %d'% rid).get('resname')) assert len(names) < 3, ("More than 2 residues with same number... " "currently unhandled. Report a bug") if len(names) > 1: if 'ACE' in names and 'NMA' in names: print("ERROR: Both ACE and NMA were given the same resid" "Check your input structure") quit(1) if 'ACE' in names: # Set ACE residue number as one less resid = atomsel('residue %d and not resname ACE' % rid).get('resid')[0] if len(atomsel("fragment '%s' and resid %d" % (frag, resid-1))): raise ValueError('ACE resid collision number %d' % (resid-1)) atomsel('residue %d and resname ACE' % rid).set('resid', resid-1) print("\tACE %d -> %d" % (resid, resid-1)) elif 'NMA' in names: # Set NMA residue number as one more resid = int(atomsel('residue %d and not resname NMA' % rid).get('resid')[0]) if len(atomsel("fragment '%s' and resid %d" % (frag, resid+1))): raise ValueError("NMA resid collision number %d" % (resid+1)) atomsel('residue %d and resname NMA' % rid).set('resid', resid+1) print("\tNMA %d -> %d" % (resid, resid+1)) # Have to save and reload so residues are parsed correctly by VMD temp = tempfile.mkstemp(suffix='_renum.mae', prefix='psf_prot_', dir=self.tmp_dir)[1] atomsel("same fragment as protein or resname ACE NMA").write('mae', temp) prot_molid = molecule.load('mae', temp) # Put things back the way they were if old_top != -1: molecule.set_top(old_top) return prot_molid
def get_cell_size(self, mem_buf, wat_buf, molid=None, filename=None, zh_mem_full=_MEMBRANE_FULL_THICKNESS / 2.0, zh_mem_hyd=_MEMBRANE_HYDROPHOBIC_THICKNESS / 2.0): """ Gets the cell size of the final system given initial system and buffers. Detects whether or not a membrane is present. Sets the size of the system. Args: mem_buf (float) : Membrane (xy) buffer amount wat_buf (float) : Water (z) buffer amount molid (int) : VMD molecule ID to consider (can't use with filename) filename (str) : Filename of system to consider (can't use w molid) zh_mem_full (float) : Membrane thickness zh_mem_hyd (float) : Membrane hydrophobic region thickness Returns: return dx_sol, dy_sol, dx_tm, dy_tm, dz_full (float tuple): x solute dimension, y solute dimension, TM x solute dimension, TM y solute dimension, solute z dimension Raises: ValueError: if filename and molid are both specified """ # Sanity check if filename is not None and molid is not None: raise ValueError("Specified molid and filename to get_cell_size") if filename is not None: top = molecule.get_top() molid = molecule.read(-1, 'mae', filename) elif molid is None: molid = molecule.get_top() # Some options different for water-only systems (no lipid) if self.water_only: solute_z = atomsel(self.solute_sel, molid=molid).get('z') dx_tm = 0.0 dy_tm = 0.0 sol_solute = atomsel(self.solute_sel, molid) else: solute_z = atomsel(self.solute_sel, molid=molid).get('z') tm_solute = atomsel('(%s) and z > %f and z < %f' % (self.solute_sel, -zh_mem_hyd, zh_mem_hyd), molid) if len(tm_solute): dx_tm = max(tm_solute.get('x')) - min(tm_solute.get('x')) dy_tm = max(tm_solute.get('y')) - min(tm_solute.get('y')) else: dx_tm = dy_tm = 0 sol_solute = atomsel('(%s) and (z < %f or z > %f)' % (self.solute_sel, -zh_mem_hyd, zh_mem_hyd), molid) # Solvent invariant options dx_sol = max(sol_solute.get('x')) - min(sol_solute.get('x')) dy_sol = max(sol_solute.get('y')) - min(sol_solute.get('y')) if self.opts.get('user_x'): self.size[0] = self.opts['user_x'] else: self.size[0] = max(dx_tm + 2.*mem_buf, dx_sol + 2.*wat_buf) if self.opts.get('user_y'): self.size[1] = self.opts['user_y'] else: self.size[1] = max(dy_tm + 2.*mem_buf, dy_sol + 2.*wat_buf) # Z dimension. If there's a membrane, need to account for asymmetry # in the Z dimension where the protein could be uneven in the membrane # or even peripheral if self.opts.get('user_z'): self.size[2] = self.opts['user_z'] buf = (self.opts['user_z'] - max(solute_z) + min(solute_z))/2 self._zmax = max(solute_z) + buf self._zmin = min(solute_z) - buf if zh_mem_full > self._zmax or -zh_mem_full < self._zmin: raise ValueError("Specified user z of %f is too small to " "accomodate protein and membrane!" % self.opts['user_z']) else: if self.water_only: self._zmax = max(solute_z) + wat_buf self._zmin = min(solute_z) - wat_buf else: self._zmax = max(max(solute_z)+wat_buf, zh_mem_full) self._zmin = min(min(solute_z)-wat_buf, -zh_mem_full) self.size[2] = self._zmax - self._zmin # Cleanup temporary file, if read in if filename is not None: molecule.delete(molid) if top != -1: molecule.set_top(top) return dx_sol, dy_sol, dx_tm, dy_tm, max(solute_z)-min(solute_z)
def get_cell_size(self, mem_buf, wat_buf, molid=None, filename=None, zh_mem_full=_MEMBRANE_FULL_THICKNESS / 2.0, zh_mem_hyd=_MEMBRANE_HYDROPHOBIC_THICKNESS / 2.0): """ Gets the cell size of the final system given initial system and buffers. Detects whether or not a membrane is present. Sets the size of the system. Args: mem_buf (float) : Membrane (xy) buffer amount wat_buf (float) : Water (z) buffer amount molid (int) : VMD molecule ID to consider (can't use with filename) filename (str) : Filename of system to consider (can't use w molid) zh_mem_full (float) : Membrane thickness zh_mem_hyd (float) : Membrane hydrophobic region thickness Returns: return dx_sol, dy_sol, dx_tm, dy_tm, dz_full (float tuple): x solute dimension, y solute dimension, TM x solute dimension, TM y solute dimension, solute z dimension Raises: ValueError: if filename and molid are both specified """ # Sanity check if filename is not None and molid is not None: raise ValueError("Specified molid and filename to get_cell_size") if filename is not None: top = molecule.get_top() molid = molecule.read(-1, 'mae', filename) elif molid is None: molid = molecule.get_top() # Some options different for water-only systems (no lipid) if self.water_only: solute_z = atomsel(self.solute_sel, molid=molid).get('z') dx_tm = 0.0 dy_tm = 0.0 sol_solute = atomsel(self.solute_sel, molid) else: solute_z = atomsel(self.solute_sel, molid=molid).get('z') tm_solute = atomsel( '(%s) and z > %f and z < %f' % (self.solute_sel, -zh_mem_hyd, zh_mem_hyd), molid) if len(tm_solute): dx_tm = max(tm_solute.get('x')) - min(tm_solute.get('x')) dy_tm = max(tm_solute.get('y')) - min(tm_solute.get('y')) else: dx_tm = dy_tm = 0 sol_solute = atomsel( '(%s) and (z < %f or z > %f)' % (self.solute_sel, -zh_mem_hyd, zh_mem_hyd), molid) # Solvent invariant options dx_sol = max(sol_solute.get('x')) - min(sol_solute.get('x')) dy_sol = max(sol_solute.get('y')) - min(sol_solute.get('y')) if self.opts.get('user_x'): self.size[0] = self.opts['user_x'] else: self.size[0] = max(dx_tm + 2. * mem_buf, dx_sol + 2. * wat_buf) if self.opts.get('user_y'): self.size[1] = self.opts['user_y'] else: self.size[1] = max(dy_tm + 2. * mem_buf, dy_sol + 2. * wat_buf) # Z dimension. If there's a membrane, need to account for asymmetry # in the Z dimension where the protein could be uneven in the membrane # or even peripheral if self.opts.get('user_z'): self.size[2] = self.opts['user_z'] buf = (self.opts['user_z'] - max(solute_z) + min(solute_z)) / 2 self._zmax = max(solute_z) + buf self._zmin = min(solute_z) - buf if zh_mem_full > self._zmax or -zh_mem_full < self._zmin: raise DabbleError("Specified user z of %f is too small to " "accomodate protein and membrane!" % self.opts['user_z']) else: if self.water_only: self._zmax = max(solute_z) + wat_buf self._zmin = min(solute_z) - wat_buf else: self._zmax = max(max(solute_z) + wat_buf, zh_mem_full) self._zmin = min(min(solute_z) - wat_buf, -zh_mem_full) self.size[2] = self._zmax - self._zmin # Cleanup temporary file, if read in if filename is not None: molecule.delete(molid) if top != -1: molecule.set_top(top) return dx_sol, dy_sol, dx_tm, dy_tm, max(solute_z) - min(solute_z)
def _write_lipids(self): """ Splits lipids into modular tail, head, tail that Lipid14 specifies. Closes the old molecule and loads the new renumbered molecule. Does name matching for lipids. Writes the pdb file with TER cards in between each lipid. Returns: (str): File name of PDB file written Raises: ValueError if an invalid lipid is found """ lipid_res = set(atomsel(self.lipid_sel).get('residue')) n_lips = len(lipid_res) if not n_lips: return None molecule.set_top(self.molid) temp = tempfile.mkstemp(suffix='.pdb', prefix='amber_lipids_', dir=self.tmp_dir)[1] fileh = open(temp, 'w') # Check if it's a normal residue first in case cholesterol etc in # the selection resid = 1 idx = 1 while lipid_res: residue = lipid_res.pop() if len(lipid_res) % 1 == 0: sys.stdout.write( "Writing lipids.... %.0f%% \r" % (100. - 100. * len(lipid_res) / float(n_lips))) sys.stdout.flush() sel = atomsel('residue %s' % residue) headres, headnam, minusidx = self.matcher.get_lipid_head(sel) # If it's not a lipid head, check if it's a normal residue if not headres: resnames, atomnames = self.matcher.get_names( sel, print_warning=False) if not resnames: raise DabbleError( "Residue %s:%s not a valid lipid" % (sel.get('resname')[0], sel.get('resid')[0])) self._apply_naming_dictionary(resnames, atomnames) sel.set('resid', resid) resid += 1 continue else: # Apply the name to the heads self._apply_naming_dictionary(headres, headnam) # Pull out the tail resnames and indices taildicts = self.matcher.get_lipid_tails(sel, headnam.keys()) for (resnames, atomnames) in taildicts: self._apply_naming_dictionary(resnames, atomnames) # Renumber the first tail, head, then second tail and write # them separately. Needs to be done this way to guarantee order. # An atom index that's in the minus tail is given by get_lipid_head. # First tail firstdict = [_ for _ in taildicts if minusidx in _[0].keys()] if len(firstdict) != 1: raise DabbleError( "Error finding tails for lipid %s:%s" % (sel.get('resname')[0], sel.get('resid')[0])) firstdict = firstdict[0] lsel = atomsel('index %s' % ' '.join([str(x) for x in \ firstdict[0].keys()])) lsel.set('resid', resid) lsel.set('user', 0.0) idx = self._write_residue(lsel, fileh, idx) taildicts.remove(firstdict) # Head lsel = atomsel('index %s' % ' '.join([str(x) for x in \ headnam.keys()])) lsel.set('resid', resid + 1) lsel.set('user', 0.0) idx = self._write_residue(lsel, fileh, idx) # Second tail lsel = atomsel('index %s' % ' '.join([str(x) for x in \ taildicts[0][0].keys()])) lsel.set('resid', resid + 2) lsel.set('user', 0.0) idx = self._write_residue(lsel, fileh, idx) resid += 3 fileh.write("TER\n") # TER card between lipid residues fileh.write("END\n") fileh.close() sys.stdout.write("\n") return temp
def _write_lipid_blocks(self): """ Writes a temporary PDB file containing the lipids for later use by psfgen. Renumbers the lipid residues because some can have **** instead of an integer for resid in large systems, which will crash psfgen. Also sets atom names for some common lipids (currently POPC) Raises: NotImplementedError if more than 10,000 lipids are present since it doesn't support feeding multiple lipid blocks to psfgen currently NotImplementedError if lipid other than POPC,POPE,POPG is found """ # Put current molecule on top to simplify atom selection old_top = molecule.get_top() molecule.set_top(self.molid) # Collect lipid residues up alll = atomsel('(%s) and user 1.0' % self.lipid_sel) residues = list(set(alll.get('residue'))) residues.sort() # Sanity check for < 10k lipids if len(residues) >= 10000: raise NotImplementedError("More than 10k lipids found") # Rename lipid residues by resname # This assumes all lipids with the same resname are the same # If that's not the case, the system is really broken in some way # for resname in set(alll.get('resname')): # ressel = atomsel("(%s) and user 1.0 and resname '%s'" # % (self.lipid_sel, resname)) # # # Get naming dictionary for one representative residue # repsel = atomsel('residue %s' % ressel.get('residue')[0]) # (newname, atomnames) = self.matcher.get_names(sel) # # # Apply naming dictionary to all of these residues # for idx, name in atomnames.items(): # oldname = atomsel('index %s' % idx).get('name') # if oldname != name: # Loop through all residues and renumber and correctly name them counter = 1 for res in residues: # Renumber residue sel = atomsel('residue %s' % res) sel.set('resid', counter) counter = counter + 1 # Rename residue # (newname, atomnames) = self.matcher.get_names(sel, # print_warning=False) # # for idx, name in atomnames.items(): # atom = atomsel('index %s' % idx) # if atom.get('name')[0] != name: # print("Renaming %s:%s: %s -> %s" % (sel.get('resname')[0], # sel.get('resid')[0], # atom.get('name')[0], # name)) # atom.set('name', name) # sel.set('resname', newname) # Write temporary lipid pdb temp = tempfile.mkstemp(suffix='.pdb', prefix='psf_lipid_', dir=self.tmp_dir)[1] alll.set('user', 0.0) alll.write('pdb', temp) # Write to file string = ''' set lipidfile %s set mid [mol new $lipidfile] segment L { first none last none pdb $lipidfile } coordpdb $lipidfile L mol delete $mid ''' % temp self.file.write(string) # Put old top back molecule.set_top(old_top)
def test_mol_attrs(file_3nob): m1 = molecule.load("mae", file_3nob) m2 = molecule.load("mae", file_3nob) # Get/set top assert molecule.get_top() == m2 molecule.set_top(molid=m1) assert molecule.get_top() == m1 with pytest.raises(ValueError): molecule.set_top(m2 + 1) # Get/set visibility molecule.set_visible(m1, visible=False) assert molecule.get_visible() == False assert molecule.get_visible(molid=m2) == True with pytest.warns(DeprecationWarning): molecule.set_visible(m1, state=True) assert molecule.get_visible(molid=m1) == True with pytest.raises(ValueError): molecule.set_visible(m2 + 1, True) with pytest.raises(TypeError): molecule.set_visible(m2, 3) with pytest.raises(ValueError): molecule.get_visible(m2 + 1) # Get/set periodic assert molecule.get_periodic(m2) == { 'a': 1.0, 'alpha': 90.0, 'b': 1.0, 'beta': 90.0, 'c': 1.0, 'gamma': 90.0 } with pytest.raises(ValueError): molecule.get_periodic(molid=m1, frame=3000) with pytest.raises(ValueError): molecule.set_periodic(molid=m2 + 1, a=2.0) with pytest.raises(ValueError): molecule.set_periodic(m1, frame=3000, a=20.0) molecule.set_periodic(m2, frame=0, a=90.0, b=90.0, c=90.0, alpha=90.0, beta=90.0, gamma=90.0) assert list(molecule.get_periodic( m2, frame=0).values()) == [pytest.approx(90.0)] * 6 assert set(molecule.get_periodic( m1, frame=0).values()) != [pytest.approx(90.0)] * 6 molecule.set_periodic(c=20.0) assert molecule.get_periodic()["c"] == pytest.approx(20.0) molecule.delete(m1) molecule.delete(m2)
def write(self, psf_name): """ Writes the pdb/psf file. Args: psf_name (str): Prefix for the pdb/psf output files, extension will be appended Returns: topologies (list of str): Topology files that were used in creating the psf """ # Clean up all temp files from previous runs if present # An earlier check will exit if it's not okay to overwrite here self.psf_name = psf_name try: os.remove('%s.pdb'% self.psf_name) os.remove('%s.psf'% self.psf_name) except OSError: pass # Finds the psfgen package and sets the output file name string = ''' set dir [file join $env(VMDDIR) plugins [vmdinfo arch] tcl psfgen1.6] package ifneeded psfgen 1.6 [list load [file join $dir libpsfgen.so]] package require psfgen set output "%s" resetpsf ''' % self.psf_name self.file.write(string) # Put our molecule on top old_top = molecule.get_top() molecule.set_top(self.molid) # Print out topology files self.file.write('\n') print("Using the following topologies:") for top in self.topologies: print(" - %s" % top.split("/")[-1]) self.file.write(' topology %s\n' % top) # Mark all atoms as unsaved with the user field atomsel('all', molid=self.molid).set('user', 1.0) check_atom_names(molid=self.molid) # Now ions if present, changing the atom names if len(atomsel('element Na Cl K', molid=self.molid)) > 0: self._write_ion_blocks() # Save water 10k molecules at a time if len(atomsel('water', molid=self.molid)): self._write_water_blocks() # Now lipid if len(atomsel(self.lipid_sel)): self._write_lipid_blocks() if not len(atomsel("resname %s" % _acids, molid=self.molid)): print("\tDidn't find any protein.\n") # Now handle the protein # Save and reload the protein so residue looping is correct prot_molid = self._renumber_protein_chains(molid=self.molid) extpatches = set() for frag in sorted(set(atomsel("resname %s" % _acids, molid=prot_molid).get('fragment'))): extpatches.update(self._write_protein_blocks(prot_molid, frag)) atomsel("same fragment as resname %s" % _acids, molid=self.molid).set("user", 0.0) # List all patches applied to the protein print("Applying the following patches:\n") print("\t%s" % "\t".join(extpatches)) self.file.write(''.join(extpatches)) self.file.write("\n") # Regenerate angles and dihedrals after applying patches # Angles must be regenerated FIRST! # See http://www.ks.uiuc.edu/Research/namd/mailing_list/namd-l.2009-2010/4137.html self.file.write("regenerate angles\nregenerate dihedrals\n") # Check if there is anything else and let the user know about it leftovers = atomsel('user 1.0', molid=self.molid) for lig in set(leftovers.get('resname')): residues = self._find_single_residue_names(resname=lig, molid=self.molid) self._write_generic_block(residues) # Write the output files and run string = ''' writepsf x-plor cmap ${output}.psf writepdb ${output}.pdb''' self.file.write(string) self.file.close() evaltcl('play %s' % self.filename) self._check_psf_output() # Reset top molecule molecule.set_top(old_top) return self.topologies
def _write_water_blocks(self): """ Writes a lot of temporary files with 10000 waters each, to bypass psfgen being stupid with files containing more than 10000 of a residue. """ # Put current molecule on top to simplify atom selection language old_top = molecule.get_top() molecule.set_top(self.molid) # Set consistent residue and atom names, crystal waters # can be named HOH, etc atomsel('water').set('resname', 'TIP3') atomsel('resname TIP3').set('chain', 'W') atomsel('resname TIP3 and element O').set('name', 'OH2') # Dowser can name water hydrogens strangely atomsel('resname TIP3 and name HW1').set('name', 'H1') atomsel('resname TIP3 and name HW2').set('name', 'H2') # Select all the waters. We'll use the user field to track which # ones have been written allw = atomsel('water and user 1.0') print("Found %d water residues" % len(set(atomsel('water and user 1.0').get('residue')))) # Find the problem waters with unordered indices problems = [] for r in set(allw.get('residue')): widx = atomsel('residue %s' % r).get("index") if max(widx) - min(widx) != 2: problems.append(r) atomsel('residue %s' % r).set("user", 0.0) # get it out of allw allw.update() num_written = int(len(allw)/(9999*3))+1 print("Going to write %d files for %d water atoms" % (num_written, len(allw))) # Pull out and write 10k waters at a time if we have normal waters if allw: for i in range(num_written): temp = tempfile.mkstemp(suffix='_%d.pdb' % i, prefix='psf_wat_', dir=self.tmp_dir)[1] residues = list(set(allw.get('residue')))[:9999] batch = atomsel('residue %s' % ' '.join([str(x) for x in residues])) try: batch.set('resid', [k for k in range(1, int(len(batch)/3)+1) for _ in range(3)]) except ValueError: print("\nERROR! You have some waters missing hydrogens!\n" "Found %d water residues, but %d water atoms. Check " " your crystallographic waters in the input structure." % (len(residues), len(batch))) quit(1) batch.set('user', 0.0) batch.write('pdb', temp) allw.update() # Now write the problem waters self._write_unorderedindex_waters(problems, self.molid) string = ''' set waterfiles [glob -directory %s psf_wat_*.pdb] set i 0 foreach watnam $waterfiles { segment W${i} { auto none first none last none pdb $watnam } coordpdb $watnam W${i} incr i } ''' % self.tmp_dir self.file.write(string) molecule.set_top(old_top) return num_written