def vacuum_pack(structure='vacuum', name='vacuum-pack', gro='vacuum-packed', pbc='nojump'): """ Pack the lipids in the plane, gently. """ gmx('grompp', base='md-%s' % name, top='vacuum', structure=structure, log='grompp-%s' % name, mdp='input-md-%s-eq-in' % name, flag='-maxwarn 100') gmx('mdrun', base='md-%s' % name, log='mdrun-%s' % name, skip=True) if pbc: remove_jump(structure='md-%s' % name, tpr='md-' + name, gro='md-%s-%s' % (name, pbc)) filecopy(wordspace['step'] + 'md-%s-%s.gro' % (name, pbc), wordspace['step'] + '%s.gro' % gro) else: filecopy(wordspace['step'] + 'md-%s' % gro, wordspace['step'] + '%s.gro' % gro) boxdims_old, boxdims = get_box_vectors(gro) wordspace['bilayer_dimensions_slab'][:2] = boxdims_old[:2]
def minimize_steep_cg(name): """ minimize_steep_cg(name) Minimization using steepest descent followed by conjugate gradient. Note that this method has been retired due to reliability issues. """ gmx('grompp', base='em-%s-steep' % name, top=name, structure=name, log='grompp-em-%s-steep' % name, mdp='input-em-steep-in') gmx('mdrun', base='em-%s-steep' % name, log='mdrun-%s-steep' % name, skip=True) #---if first step fails we skip it and try again with the second minimizer if not os.path.isfile(wordspace['step'] + 'em-%s-steep.gro'): filecopy(wordspace['step'] + '%s.gro' % name, wordspace['step'] + 'em-%s-steep.gro' % name) gmx('grompp', base='em-%s-cg' % name, top=name, structure='em-%s-steep' % name, log='grompp-%s-cg' % name, mdp='input-em-cg-in', skip=True) gmx('mdrun', base='em-%s-cg' % name, log='mdrun-%s-cg' % name) select_minimum('%s-steep' % name, '%s-cg' % name, gro='%s-minimized' % name) checkpoint()
def select_minimum(*args, **kwargs): """ select_minimum(*args,**kwargs) Select the structure with the minimum force after consecutive energy minimization steps. MOVED TO COMMON """ gro = 'confout' if 'gro' not in kwargs else kwargs['gro'] forces = {} for arg in args: #---! naming rule is applied here with open(wordspace['step'] + 'log-mdrun-' + arg, 'r') as fp: lines = fp.readlines() finished = filter(lambda x: re.match('^Maximum (f|F)orce', x), lines) if finished != []: forces[arg] = float( re.findall( '^[^=]+=\s*([^\s]+)', filter(lambda x: re.match('^Maximum (f|F)orce', x), lines).pop()).pop()) if len(forces) == 1: arg = forces.keys()[0] else: arg = filter(lambda x: forces[x] == min(forces.values()), forces.keys())[0] filecopy(wordspace['step'] + 'em-' + arg + '.gro', wordspace['step'] + gro + '.gro')
def vacuum_pack(structure='vacuum',name='vacuum-pack',gro='vacuum-packed'): """ Pack the lipids in the plane, gently. """ gmx('grompp',base='md-%s'%name,top='vacuum', structure=structure,log='grompp-%s'%name,mdp='input-md-%s-eq-in'%name, flag='-maxwarn 100') gmx('mdrun',base='md-%s'%name,log='mdrun-%s'%name,skip=True) remove_jump(structure='md-%s'%name,tpr='md-'+name,gro='md-%s-nojump'%name) filecopy(wordspace['step']+'md-%s-nojump.gro'%name,wordspace['step']+'%s.gro'%gro) boxdims_old,boxdims = get_box_vectors(gro) wordspace['bilayer_dimensions_slab'][:2] = boxdims_old[:2]
def minimize(name,method='steep',top=None): """ minimize(name,method='steep') Standard minimization procedure. """ gmx('grompp',base='em-%s-%s'%(name,method),top=name if not top else re.sub('^(.+)\.top$',r'\1',top), structure=name,log='grompp-%s-%s'%(name,method),mdp='input-em-%s-in'%method,skip=True) assert os.path.isfile(wordspace['step']+'em-%s-%s.tpr'%(name,method)) gmx('mdrun',base='em-%s-%s'%(name,method),log='mdrun-%s-%s'%(name,method)) filecopy(wordspace['step']+'em-'+'%s-%s.gro'%(name,method), wordspace['step']+'%s-minimized.gro'%name) checkpoint()
def minimize_steep_cg(name): """ minimize_steep_cg(name) Minimization using steepest descent followed by conjugate gradient. Note that this method has been retired due to reliability issues. """ gmx('grompp',base='em-%s-steep'%name,top=name,structure=name, log='grompp-em-%s-steep'%name,mdp='input-em-steep-in') gmx('mdrun',base='em-%s-steep'%name,log='mdrun-%s-steep'%name,skip=True) #---if first step fails we skip it and try again with the second minimizer if not os.path.isfile(wordspace['step']+'em-%s-steep.gro'): filecopy(wordspace['step']+'%s.gro'%name,wordspace['step']+'em-%s-steep.gro'%name) gmx('grompp',base='em-%s-cg'%name,top=name,structure='em-%s-steep'%name, log='grompp-%s-cg'%name,mdp='input-em-cg-in',skip=True) gmx('mdrun',base='em-%s-cg'%name,log='mdrun-%s-cg'%name) select_minimum('%s-steep'%name,'%s-cg'%name,gro='%s-minimized'%name) checkpoint()
def select_minimum(*args,**kwargs): """ select_minimum(*args,**kwargs) Select the structure with the minimum force after consecutive energy minimization steps. MOVED TO COMMON """ gro = 'confout' if 'gro' not in kwargs else kwargs['gro'] forces = {} for arg in args: #---! naming rule is applied here with open(wordspace['step']+'log-mdrun-'+arg,'r') as fp: lines = fp.readlines() finished = filter(lambda x: re.match('^Maximum (f|F)orce',x),lines) if finished != []: forces[arg] = float(re.findall('^[^=]+=\s*([^\s]+)', filter(lambda x: re.match('^Maximum (f|F)orce',x),lines).pop()).pop()) if len(forces)==1: arg = forces.keys()[0] else: arg = filter(lambda x: forces[x]==min(forces.values()),forces.keys())[0] filecopy(wordspace['step']+'em-'+arg+'.gro',wordspace['step']+gro+'.gro')
def minimize(name, method='steep', top=None): """ minimize(name,method='steep') Standard minimization procedure. """ gmx('grompp', base='em-%s-%s' % (name, method), top=name if not top else re.sub('^(.+)\.top$', r'\1', top), structure=name, log='grompp-%s-%s' % (name, method), mdp='input-em-%s-in' % method, skip=True) assert os.path.isfile(wordspace['step'] + 'em-%s-%s.tpr' % (name, method)) gmx('mdrun', base='em-%s-%s' % (name, method), log='mdrun-%s-%s' % (name, method)) filecopy(wordspace['step'] + 'em-' + '%s-%s.gro' % (name, method), wordspace['step'] + '%s-minimized.gro' % name) checkpoint()
def add_proteins(): """ Protein addition procedure for CGMD bilayers. """ #---assume that cgmd-protein step named the itp as follows #---! assumes only a single protein ITP for now (needs further development) protein_itp_path, = glob.glob(wordspace['last']+'/Protein*.itp') protein_itp_fn = os.path.basename(protein_itp_path) filecopy(wordspace['last']+protein_itp_fn,wordspace['step']) filecopy(wordspace['last']+wordspace['protein_ready'],wordspace['step']) filecopy(wordspace['last']+wordspace['lipid_ready'],wordspace['step']) gro_combinator(wordspace['protein_ready'],wordspace['lipid_ready'], cwd=wordspace['step'],gro='protein-lipid') adhere_protein_cgmd_bilayer(bilayer='vacuum-bilayer.gro', protein_complex='protein-lipid.gro',combo='vacuum.gro') #---assume inclusion of a partner lipid here include(protein_itp_fn) include('PIP2.itp') component('PIP2',count=wordspace['total_proteins'],top=True) component(re.findall('^(.+)\.itp$',protein_itp_fn)[0],count=wordspace['total_proteins'],top=True) #---custom additions to the mdp_specs to allow for protein groups for key in ['groups','temperature']: wordspace['mdp_specs']['input-md-npt-bilayer-eq-in.mdp'].append({key:'protein'}) if wordspace['mdp_specs']['input-md-in.mdp'] == None: wordspace['mdp_specs']['input-md-in.mdp'] = [] wordspace['mdp_specs']['input-md-in.mdp'].append({key:'protein'})
def add_proteins(): """ Protein addition procedure for CGMD bilayers. """ #---assume that cgmd-protein step named the itp as follows #---! assumes only a single protein ITP for now (needs further development) protein_itp_path, = glob.glob(wordspace['last'] + '/Protein*.itp') protein_itp_fn = os.path.basename(protein_itp_path) filecopy(wordspace['last'] + protein_itp_fn, wordspace['step']) filecopy(wordspace['last'] + wordspace['protein_ready'], wordspace['step']) filecopy(wordspace['last'] + wordspace['lipid_ready'], wordspace['step']) gro_combinator(wordspace['protein_ready'], wordspace['lipid_ready'], cwd=wordspace['step'], gro='protein-lipid') adhere_protein_cgmd_bilayer(bilayer='vacuum-bilayer.gro', protein_complex='protein-lipid.gro', combo='vacuum.gro') #---assume inclusion of a partner lipid here include(protein_itp_fn) include('PIP2.itp') component('PIP2', count=wordspace['total_proteins'], top=True) component(re.findall('^(.+)\.itp$', protein_itp_fn)[0], count=wordspace['total_proteins'], top=True) #---custom additions to the mdp_specs to allow for protein groups for key in ['groups', 'temperature']: wordspace['mdp_specs']['input-md-npt-bilayer-eq-in.mdp'].append( {key: 'protein'}) if wordspace['mdp_specs']['input-md-in.mdp'] == None: wordspace['mdp_specs']['input-md-in.mdp'] = [] wordspace['mdp_specs']['input-md-in.mdp'].append({key: 'protein'})
def solvate(structure,top): """ solvate(structure,top) Standard solvate procedure for atomistic protein in water. """ #---purge the wordspace of solvent and anions in case we are resuming for key in [wordspace['anion'],wordspace['cation'],'SOL']: if key in zip(*wordspace['composition'])[0]: del wordspace['composition'][zip(*wordspace['composition'])[0].index(key)] gmx('editconf',structure=structure,gro='solvate-box-alone', log='editconf-checksize',flag='-d 0') with open(wordspace['step']+'log-editconf-checksize','r') as fp: lines = fp.readlines() boxdims = map(lambda y:float(y),re.findall('\s*box vectors \:\s*([^\s]+)\s+([^\s]+)\s+([^\s]+)', filter(lambda x:re.match('\s*box vectors',x),lines).pop()).pop()) boxvecs = tuple([i+2*wordspace['water_buffer'] for i in boxdims]) center = tuple([i/2. for i in boxvecs]) #---cube is not implemented yet gmx('editconf',structure=structure,gro='solvate-protein', flags='-center %f %f %f'%center+' '+'-box %f %f %f'%boxvecs, log='editconf-center-protein') gmx('genbox',structure='solvate-protein',solvent=wordspace['solvent'], gro='solvate-dense',#top='solvate-standard', log='genbox-solvate') #---trim waters if the protein_water_gap setting is not False if 'protein_water_gap' in wordspace and wordspace['protein_water_gap'] != False: trim_waters(structure='solvate-dense',gro='solvate', gap=wordspace['protein_water_gap'],boxvecs=boxvecs) else: filecopy(wordspace['step']+'solvate-dense.gro',wordspace['step']+'solvate.gro') gmx('make_ndx',structure='solvate',ndx='solvate-water-check',inpipe='q\n', log='make-ndx-solvate-check') with open(wordspace['step']+'log-make-ndx-solvate-check','r') as fp: lines = fp.readlines() nwaters = int(re.findall('\s*[0-9]+\s+Water\s+:\s+([0-9]+)\s+atoms', filter(lambda x:re.match('\s*[0-9]+\s+Water',x),lines).pop()).pop())/3 wordspace['water_without_ions'] = nwaters component('SOL',count=nwaters) #---add the suffix so that water is referred to by its name in the settings include(wordspace['water'],ff=True) write_top('solvate.top')
def trim_waters(structure='solvate-dense',gro='solvate', gap=3,boxvecs=None,method='aamd',boxcut=True): """ trim_waters(structure='solvate-dense',gro='solvate',gap=3,boxvecs=None) Remove waters within a certain number of Angstroms of the protein. #### water and all (water and (same residue as water within 10 of not water)) note that we vided the solvate.gro as a default so this can be used with any output gro file """ use_vmd = wordspace.get('use_vmd',False) if (gap != 0.0 or boxcut) and use_vmd: if method == 'aamd': watersel = "water" elif method == 'cgmd': watersel = "resname %s"%wordspace.sol else: raise Exception("\n[ERROR] unclear method %s"%method) #---! gap should be conditional and excluded if zero vmdtrim = [ 'package require pbctools', 'mol new %s.gro'%structure, 'set sel [atomselect top \"(all not ('+\ '%s and (same residue as %s and within '%(watersel,watersel)+str(gap)+\ ' of not %s)))'%watersel] #---box trimming is typical for e.g. atomstic protein simulations but discards anything outside if boxcut: vmdtrim += [' and '+\ 'same residue as (x>=0 and x<='+str(10*boxvecs[0])+\ ' and y>=0 and y<= '+str(10*boxvecs[1])+\ ' and z>=0 and z<= '+str(10*boxvecs[2])+')'] vmdtrim += ['"]','$sel writepdb %s-vmd.pdb'%gro,'exit',] with open(wordspace['step']+'script-vmd-trim.tcl','w') as fp: for line in vmdtrim: fp.write(line+'\n') vmdlog = open(wordspace['step']+'log-script-vmd-trim','w') #---previously used os.environ['VMDNOCUDA'] = "1" but this was causing segfaults on green p = subprocess.Popen('VMDNOCUDA=1 '+gmxpaths['vmd']+' -dispdev text -e script-vmd-trim.tcl', stdout=vmdlog,stderr=vmdlog,cwd=wordspace['step'],shell=True,executable='/bin/bash') p.communicate() with open(wordspace['bash_log'],'a') as fp: fp.write(gmxpaths['vmd']+' -dispdev text -e script-vmd-trim.tcl &> log-script-vmd-trim\n') gmx_run(gmxpaths['editconf']+' -f %s-vmd.pdb -o %s.gro -resnr 1'%(gro,gro), log='editconf-convert-vmd') #---scipy is more reliable than VMD elif gap != 0.0 or boxcut: import scipy import scipy.spatial import numpy as np #---if "sol" is not in the wordspace we assume this is atomistic and use the standard "SOL" watersel = wordspace.get('sol','SOL') incoming = read_gro(structure+'.gro') #---remove waters that are near not-waters is_water = np.array(incoming['residue_names'])==watersel is_not_water = np.array(incoming['residue_names'])!=watersel water_inds = np.where(is_water)[0] not_water_inds = np.where(np.array(incoming['residue_names'])!=watersel)[0] points = np.array(incoming['points']) residue_indices = np.array(incoming['residue_indices']) if gap>0: #---previous method used clumsy/slow cdist if False: #---! needs KDTree optimization dists = scipy.spatial.distance.cdist(points[water_inds],points[not_water_inds]) #---list of residue indices in is_water that have at least one atom with an overlap excludes = np.array(incoming['residue_indices'])[is_water][ np.where(np.any(dists<=gap/10.0,axis=1))[0]] #---collect waters not found in the excludes list of residues that overlap with not-water #---note that this command fails on redundant residues #---this was deprecated because it wasn't working correctly with the new KDTree method below surviving_water = np.all((np.all(( np.tile(excludes,(len(residue_indices),1))!=np.tile(residue_indices,(len(excludes),1)).T), axis=1),is_water),axis=0) #---use scipy KDTree to find atom names inside the gap #---note that order matters: we wish to find waters too close to not_waters close_dists,neighbors = scipy.spatial.KDTree(points[water_inds]).query(points[not_water_inds],distance_upper_bound=gap/10.0) #---use the distances to find the residue indices for waters that are too close excludes = np.array(incoming['residue_indices'])[is_water][np.where(close_dists<=gap/10.0)[0]] #---get residues that are water and in the exclude list #---note that the following step might be slow exclude_res = [ii for ii,i in enumerate(incoming['residue_indices']) if i in excludes and is_water[ii]] #---copy the array that marks the waters surviving_water = np.array(is_water) #---remove waters that are on the exclude list surviving_water[exclude_res] = False else: excludes = np.array([]) surviving_water = np.ones(len(residue_indices)).astype(bool) #---we must remove waters that lie outside the box if there is a boxcut insiders = np.ones(len(points)).astype(bool) if boxcut: #---remove waters that lie outside the box #---get points that are outside of the box outsiders = np.any([np.any((points[:,ii]<0,points[:,ii]>i),axis=0) for ii,i in enumerate(boxvecs)],axis=0) #---get residue numbers for the outsiders outsiders_res = np.array(incoming['residue_indices'])[np.where(outsiders)[0]] #---note that this is consonant with the close-water exclude step above (and also may be slow) exclude_outsider_res = [ii for ii,i in enumerate(incoming['residue_indices']) if i in outsiders_res] insiders[exclude_outsider_res] = False surviving_indices = np.any((is_not_water,np.all((surviving_water,insiders),axis=0)),axis=0) lines = incoming['lines'] lines = lines[:2]+list(np.array(incoming['lines'][2:-1])[surviving_indices])+lines[-1:] xyzs = list(points[surviving_indices]) write_gro(lines=lines,xyzs=xyzs,output_file=wordspace.step+'%s.gro'%gro) else: filecopy(wordspace['step']+'%s-dense.gro'%gro,wordspace['step']+'%s.gro'%gro)
def solvate(structure, top): """ solvate(structure,top) Standard solvate procedure for atomistic protein in water. """ #---purge the wordspace of solvent and anions in case we are resuming for key in [wordspace['anion'], wordspace['cation'], 'SOL']: if key in zip(*wordspace['composition'])[0]: del wordspace['composition'][zip( *wordspace['composition'])[0].index(key)] gmx('editconf', structure=structure, gro='solvate-box-alone', log='editconf-checksize', flag='-d 0') with open(wordspace['step'] + 'log-editconf-checksize', 'r') as fp: lines = fp.readlines() boxdims = map( lambda y: float(y), re.findall( '\s*box vectors \:\s*([^\s]+)\s+([^\s]+)\s+([^\s]+)', filter(lambda x: re.match('\s*box vectors', x), lines).pop()).pop()) boxvecs = tuple([i + 2 * wordspace['water_buffer'] for i in boxdims]) center = tuple([i / 2. for i in boxvecs]) #---cube is not implemented yet gmx('editconf', structure=structure, gro='solvate-protein', flags='-center %f %f %f' % center + ' ' + '-box %f %f %f' % boxvecs, log='editconf-center-protein') gmx( 'genbox', structure='solvate-protein', solvent=wordspace['solvent'], gro='solvate-dense', #top='solvate-standard', log='genbox-solvate') #---trim waters if the protein_water_gap setting is not False if 'protein_water_gap' in wordspace and wordspace[ 'protein_water_gap'] != False: trim_waters(structure='solvate-dense', gro='solvate', gap=wordspace['protein_water_gap'], boxvecs=boxvecs) else: filecopy(wordspace['step'] + 'solvate-dense.gro', wordspace['step'] + 'solvate.gro') gmx('make_ndx', structure='solvate', ndx='solvate-water-check', inpipe='q\n', log='make-ndx-solvate-check') with open(wordspace['step'] + 'log-make-ndx-solvate-check', 'r') as fp: lines = fp.readlines() nwaters = int( re.findall( '\s*[0-9]+\s+Water\s+:\s+([0-9]+)\s+atoms', filter(lambda x: re.match('\s*[0-9]+\s+Water', x), lines).pop()).pop()) / 3 wordspace['water_without_ions'] = nwaters component('SOL', count=nwaters) #---add the suffix so that water is referred to by its name in the settings include(wordspace['water'], ff=True) write_top('solvate.top')
def multiply(nx=1,ny=1,nz=1,quirky_ions=True): """ Make a copy of a simulation box in multiple directions. """ factor = nx*ny*nz #---update the composition #---if the last step doesn't have the composition we step backwards and pick up requirements #---note that "protein_ready" is important for the bilayer_sorter for prereq in ['composition','lipids','cation','anion','protein_ready']: if prereq not in wordspace: steplist = detect_last(steplist=True)[::-1] #---walk backwards through steps until we find the commposition for ii,i in enumerate(steplist): oldspace = resume(read_only=True,step=int(re.match('s([0-9]+)-',i).group(1))) if prereq in oldspace: wordspace[prereq] = deepcopy(oldspace[prereq]) break #---if composition is available we continue wordspace['new_composition'] = [[name,count*factor] for name,count in wordspace['composition']] kwargs = {} if 'buffer' in wordspace: kwargs['flag'] = ' -dist %.2f %.2f %.2f'%tuple(wordspace['buffer']) gmx('genconf',structure='system-input',gro='system-multiply', nbox="%d %d %d"%(nx,ny,nz),log='genconf-multiply',**kwargs) #---copy ITP files for itp in wordspace.itp: filecopy(wordspace.last_step+itp,wordspace.step+itp) #---reorder the GRO for convenience with open(wordspace['step']+'system-multiply.gro') as fp: lines = fp.readlines() #---collect all unique resiue/atom combinations combos = list(set([l[5:15] for l in lines])) #---for each element in the composition, extract all of the residues for that element lines_reorder = [] lines_reorder.extend(lines[:2]) #---develop a list of filtering rules keylist = {} for key,count in wordspace['new_composition']: if key in [wordspace[i] for i in ['anion','cation']]: keylist[key] = 'regex',(('ION',key),slice(5,15),'\s*%s\s*%s\s*') elif re.match('^(p|P)rotein',key) and key+'.itp' in wordspace.itp: #---custom procedure for finding proteins which have variegated residue numbers itp = read_itp(wordspace.step+key+'.itp') residues_starts = [] seq = list(zip(*itp['atoms'])[3]) residues = [i[5:10].strip() for i in lines] for i in range(len(residues)-len(seq)): #---minor speed up by checking the first one if seq[0]==residues[i] and residues[i:i+len(seq)]==seq: residues_starts.append(i) keylist[key] = 'slices',[slice(i,i+len(seq)) for i in residues_starts] else: keylist[key] = 'regex',(key,slice(5,10),'\s*%s\s*') for key,count in wordspace['new_composition']: method,details = keylist[key] if method == 'regex': key,sl,regex = details lines_reorder.extend([l for l in lines[2:-1] if re.match(regex%key,l[sl])]) elif method == 'slices': for sl in details: lines_reorder.extend(lines[sl]) else: raise lines_reorder.extend([lines[-1]]) with open(wordspace['step']+'system-multiply-reorder.gro','w') as fp: for line in lines_reorder: fp.write(line) filecopy(wordspace['step']+'system-multiply-reorder.gro',wordspace['step']+'system.gro') wordspace['composition'] = tuple(wordspace['new_composition']) del wordspace['new_composition']
def trim_waters(structure='solvate-dense', gro='solvate', gap=3, boxvecs=None, method='aamd', boxcut=True): """ trim_waters(structure='solvate-dense',gro='solvate',gap=3,boxvecs=None) Remove waters within a certain number of Angstroms of the protein. #### water and all (water and (same residue as water within 10 of not water)) note that we vided the solvate.gro as a default so this can be used with any output gro file """ use_vmd = wordspace.get('use_vmd', False) if (gap != 0.0 or boxcut) and use_vmd: if method == 'aamd': watersel = "water" elif method == 'cgmd': watersel = "resname %s" % wordspace.sol else: raise Exception("\n[ERROR] unclear method %s" % method) #---! gap should be conditional and excluded if zero vmdtrim = [ 'package require pbctools', 'mol new %s.gro'%structure, 'set sel [atomselect top \"(all not ('+\ '%s and (same residue as %s and within '%(watersel,watersel)+str(gap)+\ ' of not %s)))'%watersel] #---box trimming is typical for e.g. atomstic protein simulations but discards anything outside if boxcut: vmdtrim += [' and '+\ 'same residue as (x>=0 and x<='+str(10*boxvecs[0])+\ ' and y>=0 and y<= '+str(10*boxvecs[1])+\ ' and z>=0 and z<= '+str(10*boxvecs[2])+')'] vmdtrim += [ '"]', '$sel writepdb %s-vmd.pdb' % gro, 'exit', ] with open(wordspace['step'] + 'script-vmd-trim.tcl', 'w') as fp: for line in vmdtrim: fp.write(line + '\n') vmdlog = open(wordspace['step'] + 'log-script-vmd-trim', 'w') #---previously used os.environ['VMDNOCUDA'] = "1" but this was causing segfaults on green p = subprocess.Popen('VMDNOCUDA=1 ' + gmxpaths['vmd'] + ' -dispdev text -e script-vmd-trim.tcl', stdout=vmdlog, stderr=vmdlog, cwd=wordspace['step'], shell=True, executable='/bin/bash') p.communicate() with open(wordspace['bash_log'], 'a') as fp: fp.write( gmxpaths['vmd'] + ' -dispdev text -e script-vmd-trim.tcl &> log-script-vmd-trim\n' ) gmx_run(gmxpaths['editconf'] + ' -f %s-vmd.pdb -o %s.gro -resnr 1' % (gro, gro), log='editconf-convert-vmd') #---scipy is more reliable than VMD elif gap != 0.0 or boxcut: import scipy import scipy.spatial import numpy as np #---if "sol" is not in the wordspace we assume this is atomistic and use the standard "SOL" watersel = wordspace.get('sol', 'SOL') incoming = read_gro(structure + '.gro') #---remove waters that are near not-waters is_water = np.array(incoming['residue_names']) == watersel is_not_water = np.array(incoming['residue_names']) != watersel water_inds = np.where(is_water)[0] not_water_inds = np.where( np.array(incoming['residue_names']) != watersel)[0] points = np.array(incoming['points']) residue_indices = np.array(incoming['residue_indices']) if gap > 0: #---previous method used clumsy/slow cdist if False: #---! needs KDTree optimization dists = scipy.spatial.distance.cdist(points[water_inds], points[not_water_inds]) #---list of residue indices in is_water that have at least one atom with an overlap excludes = np.array( incoming['residue_indices'])[is_water][np.where( np.any(dists <= gap / 10.0, axis=1))[0]] #---collect waters not found in the excludes list of residues that overlap with not-water #---note that this command fails on redundant residues #---this was deprecated because it wasn't working correctly with the new KDTree method below surviving_water = np.all((np.all( (np.tile(excludes, (len(residue_indices), 1)) != np.tile( residue_indices, (len(excludes), 1)).T), axis=1), is_water), axis=0) #---use scipy KDTree to find atom names inside the gap #---note that order matters: we wish to find waters too close to not_waters close_dists, neighbors = scipy.spatial.KDTree( points[water_inds]).query(points[not_water_inds], distance_upper_bound=gap / 10.0) #---use the distances to find the residue indices for waters that are too close excludes = np.array( incoming['residue_indices'])[is_water][np.where( close_dists <= gap / 10.0)[0]] #---get residues that are water and in the exclude list #---note that the following step might be slow exclude_res = [ ii for ii, i in enumerate(incoming['residue_indices']) if i in excludes and is_water[ii] ] #---copy the array that marks the waters surviving_water = np.array(is_water) #---remove waters that are on the exclude list surviving_water[exclude_res] = False else: excludes = np.array([]) surviving_water = np.ones(len(residue_indices)).astype(bool) #---we must remove waters that lie outside the box if there is a boxcut insiders = np.ones(len(points)).astype(bool) if boxcut: #---remove waters that lie outside the box #---get points that are outside of the box outsiders = np.any([ np.any((points[:, ii] < 0, points[:, ii] > i), axis=0) for ii, i in enumerate(boxvecs) ], axis=0) #---get residue numbers for the outsiders outsiders_res = np.array( incoming['residue_indices'])[np.where(outsiders)[0]] #---note that this is consonant with the close-water exclude step above (and also may be slow) exclude_outsider_res = [ ii for ii, i in enumerate(incoming['residue_indices']) if i in outsiders_res ] insiders[exclude_outsider_res] = False surviving_indices = np.any( (is_not_water, np.all((surviving_water, insiders), axis=0)), axis=0) lines = incoming['lines'] lines = lines[:2] + list( np.array(incoming['lines'][2:-1])[surviving_indices]) + lines[-1:] xyzs = list(points[surviving_indices]) write_gro(lines=lines, xyzs=xyzs, output_file=wordspace.step + '%s.gro' % gro) else: filecopy(wordspace['step'] + '%s-dense.gro' % gro, wordspace['step'] + '%s.gro' % gro)
def multiply(nx=1, ny=1, nz=1, quirky_ions=True): """ Make a copy of a simulation box in multiple directions. """ factor = nx * ny * nz #---update the composition #---if the last step doesn't have the composition we step backwards and pick up requirements #---note that "protein_ready" is important for the bilayer_sorter for prereq in [ 'composition', 'lipids', 'cation', 'anion', 'protein_ready' ]: if prereq not in wordspace: steplist = detect_last(steplist=True)[::-1] #---walk backwards through steps until we find the commposition for ii, i in enumerate(steplist): oldspace = resume(read_only=True, step=int(re.match('s([0-9]+)-', i).group(1))) if prereq in oldspace: wordspace[prereq] = deepcopy(oldspace[prereq]) break #---if composition is available we continue wordspace['new_composition'] = [[name, count * factor] for name, count in wordspace['composition'] ] kwargs = {} if 'buffer' in wordspace: kwargs['flag'] = ' -dist %.2f %.2f %.2f' % tuple(wordspace['buffer']) gmx('genconf', structure='system-input', gro='system-multiply', nbox="%d %d %d" % (nx, ny, nz), log='genconf-multiply', **kwargs) #---copy ITP files for itp in wordspace.itp: filecopy(wordspace.last_step + itp, wordspace.step + itp) #---reorder the GRO for convenience with open(wordspace['step'] + 'system-multiply.gro') as fp: lines = fp.readlines() #---collect all unique resiue/atom combinations combos = list(set([l[5:15] for l in lines])) #---for each element in the composition, extract all of the residues for that element lines_reorder = [] lines_reorder.extend(lines[:2]) #---develop a list of filtering rules keylist = {} for key, count in wordspace['new_composition']: if key in [wordspace[i] for i in ['anion', 'cation']]: keylist[key] = 'regex', (('ION', key), slice(5, 15), '\s*%s\s*%s\s*') elif re.match('^(p|P)rotein', key) and key + '.itp' in wordspace.itp: #---custom procedure for finding proteins which have variegated residue numbers itp = read_itp(wordspace.step + key + '.itp') residues_starts = [] seq = list(zip(*itp['atoms'])[3]) residues = [i[5:10].strip() for i in lines] for i in range(len(residues) - len(seq)): #---minor speed up by checking the first one if seq[0] == residues[i] and residues[i:i + len(seq)] == seq: residues_starts.append(i) keylist[key] = 'slices', [ slice(i, i + len(seq)) for i in residues_starts ] else: keylist[key] = 'regex', (key, slice(5, 10), '\s*%s\s*') for key, count in wordspace['new_composition']: method, details = keylist[key] if method == 'regex': key, sl, regex = details lines_reorder.extend( [l for l in lines[2:-1] if re.match(regex % key, l[sl])]) elif method == 'slices': for sl in details: lines_reorder.extend(lines[sl]) else: raise lines_reorder.extend([lines[-1]]) with open(wordspace['step'] + 'system-multiply-reorder.gro', 'w') as fp: for line in lines_reorder: fp.write(line) filecopy(wordspace['step'] + 'system-multiply-reorder.gro', wordspace['step'] + 'system.gro') wordspace['composition'] = tuple(wordspace['new_composition']) del wordspace['new_composition']