diff --git a/tools/polybond/Manual.pdf b/tools/polybond/Manual.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4c5db6025a2007536416d209830ecf4a3c240dd1 Binary files /dev/null and b/tools/polybond/Manual.pdf differ diff --git a/tools/polybond/README b/tools/polybond/README new file mode 100644 index 0000000000000000000000000000000000000000..7149103d127dac2dd2b711202e7d2c11f37ce813 --- /dev/null +++ b/tools/polybond/README @@ -0,0 +1,12 @@ +25 July 2013 + +This directory contains a Python-based tool for performing +"programmable polymer bonding". The Python file lmpsdata.py provides a +"Lmpsdata" class with various methods which can be invoked by a +user-written Python script to create data files with complex bonding +topologies. + +See the Manual.pdf for details and example scripts. + +This tool and the manual was written by Zachary Kraus at Georgia Tech. +He can be contacted at this email address: zachkraus at gatech.edu. diff --git a/tools/polybond/lmpsdata.py b/tools/polybond/lmpsdata.py new file mode 100644 index 0000000000000000000000000000000000000000..36cd6a0aa31c5550c6a76c8e3868156f8fa5ddda --- /dev/null +++ b/tools/polybond/lmpsdata.py @@ -0,0 +1,1945 @@ +# +# +# lmpsdata.py +# +# For reading, writing and manipulating lammps data files +# For calculation of certain properities using lammps data files +# For creating VMD input text files using lammps data files +# All x,y,z calculations assume the information includes image flags + +class Lmpsdata: + def __init__(self,file,atomtype): + """initiates lammps data structures""" + self.atomtype=atomtype + self.keywords=[] + self.atoms=[] + self.angles=[] + self.bonds=[] + self.dihedrals=[] + self.dipoles=[] + self.impropers=[] + self.masses=[] + self.shapes=[] + self.velocities=[] + self.anglecoef=[] + self.bondcoef=[] + self.dihedralcoef=[] + self.impropercoef=[] + self.paircoef=[] + self.read(file) + + def read(self,file): + """Reads in lammps data file + Skips the first line of the file (Comment line) + First reads the header portion of the file (sectflag=1) + blank lines are skipped + header keywords delineate an assignment + if no header keyword is found, body portion begins + Second reads the body portion of the file (sectflag=2) + first line of a section has only a keyword + next line is skipped + remaining lines contain values + a blank line signifies the end of that section + if no value is listed on a line than a keyword must be used + File is read until the end + The keywords read in are stored in self.keywords""" + if file=='': + print 'no file is given. Will have to build keywords and class structures manually' + return + sectflag=1 + f=open(file,'r') + f.readline() + for line in f: + row=line.split() + if sectflag==1: + if len(row)==0: + #skip line the line is blank + pass + elif len(row)==1: + # Set Sectflag to 2, assume line is a keyword + sectflag=2 + checkkey=1 #ensures keyword will be checked in body portion + keyword=row + elif len(row)==2: + if row[1]=='atoms': + self.atomnum=row[0] + self.keywords.append('atoms') + elif row[1]=='bonds': + self.bondnum=row[0] + self.keywords.append('bonds') + elif row[1]=='angles': + self.anglenum=row[0] + self.keywords.append('angles') + elif row[1]=='dihedrals': + self.dihedralnum=row[0] + self.keywords.append('dihedrals') + elif row[1]=='impropers': + self.impropernum=row[0] + self.keywords.append('impropers') + else: + # Set Sectflag to 2, assume line is a keyword + sectflag=2 + checkkey=1 #ensures keyword will be checked in body portion + keyword=row + elif len(row)==3: + if row[1]=='atom' and row[2]=='types': + self.atomtypenum=row[0] + self.keywords.append('atom types') + elif row[1]=='bond' and row[2]=='types': + self.bondtypenum=row[0] + self.keywords.append('bond types') + elif row[1]=='angle' and row[2]=='types': + self.angletypenum=row[0] + self.keywords.append('angle types') + elif row[1]=='dihedral' and row[2]=='types': + self.dihedraltypenum=row[0] + self.keywords.append('dihedral types') + elif row[1]=='improper' and row[2]=='types': + self.impropertypenum=row[0] + self.keywords.append('improper types') + else: + # Set Sectflag to 2, assume line is a keyword + sectflag=2 + checkkey=1 #ensures keyword will be checked in body portion + keyword=row + elif len(row)==4: + if row[2]=='xlo' and row[3]=='xhi': + self.xdimension=[row[0], row[1]] + self.keywords.append('xlo xhi') + elif row[2]=='ylo' and row[3]=='yhi': + self.ydimension=[row[0], row[1]] + self.keywords.append('ylo yhi') + elif row[2]=='zlo' and row[3]=='zhi': + self.zdimension=[row[0], row[1]] + self.keywords.append('zlo zhi') + else: + # Set Sectflag to 2, assume line is a keyword + sectflag=2 + checkkey=1 #ensures keyword will be checked in body portion + keyword=row + elif len(row)==5: + if row[1]=='extra' and row[2]=='bond' and row[3]=='per' and row[4]=='atom': + self.extrabonds=row[0] + self.keywords.append('extra bond per atom') + else: + # Set Sectflag to 2, assume line is a keyword + sectflag=2 + checkkey=1 #ensures keyword will be checked in body portion + keyword=row + elif len(row)==6: + if row[3]=='xy' and row[4]=='xz' and row[5]=='yz': + self.tilt=[row[0], row[1], row[2]] + self.keywords.append('xy xz yz') + else: + # Set Sectflag to 2, assume line is a keyword + sectflag=2 + checkkey=1 #ensures keyword will be checked in body portion + keyword=row + else: + # set sectflag to 2, assume line is a keyword + sectflag=2 + checkkey=1 #ensures keyword will be checked in body portion + keyword=row + elif sectflag==2: + if checkkey==1: + if len(keyword)==1: + if keyword[0]=='Atoms' or keyword[0]=='Velocities' or keyword[0]=='Masses' or\ + keyword[0]=='Shapes' or keyword[0]=='Dipoles' or keyword[0]=='Bonds' or\ + keyword[0]=='Angles' or keyword[0]=='Dihedrals' or keyword[0]=='Impropers': + bodyflag=1 + blanknum=0 + self.keywords.append(keyword[0]) + checkkey=0 + else: + bodyflag=0 + checkkey=0 + elif len(keyword)==2: + if row[1]=='Coeffs' and (row[0]=='Pair' or row[0]=='Bond' or row[0]=='Angle' or\ + row[0]=='Dihedral' or row[0]=='Improper'):#class 2 force field keywords not included + bodyflag=1 + blanknum=0 + self.keywords.append('{0} {1}'.format(keyword[0],keyword[1])) + checkkey=0 + else: + bodyflag=0 + checkkey=0 + else: + #egnore line and set bodyflag=0 + bodyflag=0 + checkkey=0 + if bodyflag==0: #bodyflag 0 means no body keyword has been found + if len(row)==1: + if row[0]=='Atoms' or row[0]=='Velocities' or row[0]=='Masses' or\ + row[0]=='Shapes' or row[0]=='Dipoles' or row[0]=='Bonds' or\ + row[0]=='Angles' or row[0]=='Dihedrals' or row[0]=='Impropers': + bodyflag=1 + blanknum=0 + keyword=row + self.keywords.append(keyword[0]) + else: + #egnore line + pass + elif len(row)==2: + if row[1]=='Coeffs' and (row[0]=='Pair' or row[0]=='Bond' or row[0]=='Angle' or\ + row[0]=='Dihedral' or row[0]=='Improper'):#class 2 force field keywords not included + bodyflag=1 + blanknum=0 + keyword=row + self.keywords.append('{0} {1}'.format(keyword[0],keyword[1])) + else: + #egnore line + pass + else: + #egnore line + pass + elif bodyflag==1: #currently assumes 1 or more blank lines are between body data keywords + if len(row)==0: + blanknum+=1 + if blanknum>1: + bodyflag=0 + elif len(keyword)==1: + if keyword[0]=='Atoms': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.atoms.append(row) + elif keyword[0]=='Velocities': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.velocities.append(row) + elif keyword[0]=='Masses': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.masses.append(row) + elif keyword[0]=='Shapes': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.shapes.append(row) + elif keyword[0]=='Dipoles': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.dipoles.append(row) + elif keyword[0]=='Bonds': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.bonds.append(row) + elif keyword[0]=='Angles': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.angles.append(row) + elif keyword[0]=='Dihedrals': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.dihedrals.append(row) + elif keyword[0]=='Impropers': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.impropers.append(row) + else: + #egnore line and change bodyflag to 0 + bodyflag=0 + elif len(keyword)==2: + if keyword[0]=='Pair' and keyword[1]=='Coeffs': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.paircoef.append(row) + elif keyword[0]=='Bond' and keyword[1]=='Coeffs': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.bondcoef.append(row) + elif keyword[0]=='Angle' and keyword[1]=='Coeffs': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.anglecoef.append(row) + elif keyword[0]=='Dihedral' and keyword[1]=='Coeffs': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.dihedralcoef.append(row) + elif keyword[0]=='Improper' and keyword[1]=='Coeffs': + try: + int(row[0]) + except ValueError: + keyword=row + checkkey=1 + else: + self.impropercoef.append(row) + else: + #egnore line and change bodyflag to 0 + bodyflag=0 + else: + #egnore line and change bodyflag to 0 + bodyflag=0 + f.close() + + def write(self,file,modflag): + """Write lammps data files using the lammps keywords and lammpsdata structures + writes first line of the file as a Comment line + if no modifications to any of the lammpsdata structures (modflag=0) + Use Keywords to write lammpsdata structures directly + if modifications to any of the lammpsdata structures (modflag=1) + Key Lammpsdata structures like atom numbers, coeficient numbers + need to be modified to match the other modified lammpsdata structures + This section will still use the keywords to write lammpsdata structures. + For all modflags, the code will write data to the file until all of the + keyword's data structures have been finished writing. + The keywords are stored in self.keywords""" + f=open(file,'w') + f.write('polymer data file\n') + for row in self.keywords: + if row=='atoms': + if modflag==0: + f.write('{0} {1}\n'.format(self.atomnum,row)) + elif modflag==1: + f.write('{0} {1}\n'.format(len(self.atoms),row)) + elif row=='bonds': + if modflag==0: + f.write('{0} {1}\n'.format(self.bondnum,row)) + elif modflag==1: + f.write('{0} {1}\n'.format(len(self.bonds),row)) + elif row=='angles': + if modflag==0: + f.write('{0} {1}\n'.format(self.anglenum,row)) + elif modflag==1: + f.write('{0} {1}\n'.format(len(self.angles),row)) + elif row=='dihedrals': + if modflag==0: + f.write('{0} {1}\n'.format(self.dihedralnum,row)) + elif modflag==1: + f.write('{0} {1}\n'.format(len(self.dihedrals),row)) + elif row=='impropers': + if modflag==0: + f.write('{0} {1}\n'.format(self.impropernum,row)) + elif modflag==1: + f.write('{0} {1}\n'.format(len(self.impropers),row)) + elif row=='atom types': + if modflag==0: + f.write('{0} {1}\n'.format(self.atomtypenum,row)) + elif modflag==1: + f.write('{0} {1}\n'.format(len(self.masses),row)) + elif row=='bond types': + if modflag==0: + f.write('{0} {1}\n'.format(self.bondtypenum,row)) + elif modflag==1: + f.write('{0} {1}\n'.format(len(self.bondcoef),row)) + elif row=='angle types': + if modflag==0: + f.write('{0} {1}\n'.format(self.angletypenum,row)) + elif modflag==1: + f.write('{0} {1}\n'.format(len(self.anglecoef),row)) + elif row=='dihedral types': + if modflag==0: + f.write('{0} {1}\n'.format(self.dihedraltypenum,row)) + elif modflag==1: + f.write('{0} {1}\n'.format(len(self.dihedralcoef),row)) + elif row=='improper types': + if modflag==0: + f.write('{0} {1}\n'.format(self.impropertypenum,row)) + elif modflag==1: + f.write('{0} {1}\n'.format(len(self.impropercoef),row)) + elif row=='xlo xhi': + f.write('{0} {1} {2}\n'.format(self.xdimension[0],self.xdimension[1],row)) + elif row=='ylo yhi': + f.write('{0} {1} {2}\n'.format(self.ydimension[0],self.ydimension[1],row)) + elif row=='zlo zhi': + f.write('{0} {1} {2}\n'.format(self.zdimension[0],self.zdimension[1],row)) + elif row=='extra bond per atom': + f.write('{0} {1}\n'.format(self.extrabonds,row)) + elif row=='xy xz yz': + f.write('{0} {1} {2} {3}\n'.format(self.tilt[0],self.tilt[1],self.tilt[2],row)) + elif row=='Atoms': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.atoms: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + f.write('\n') #allows space to be added between the end of body data and a new keyword + elif row=='Velocities': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.velocities: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + f.write('\n') #allows space to be added between the end of body data and a new keyword + elif row=='Masses': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.masses: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + f.write('\n') #allows space to be added between the end of body data and a new keyword + elif row=='Shapes': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.shapes: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + f.write('\n') #allows space to be added between the end of body data and a new keyword + elif row=='Dipoles': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.dipoles: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + f.write('\n') #allows space to be added between the end of body data and a new keyword + elif row=='Bonds': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.bonds: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + f.write('\n') #allows space to be added between the end of body data and a new keyword + elif row=='Angles': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.angles: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + f.write('\n') #allows space to be added between the end of body data and a new keyword + elif row=='Dihedrals': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.dihedrals: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + elif row=='Impropers': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.impropers: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + f.write('\n') #allows space to be added between the end of body data and a new keyword + elif row=='Pair Coeffs': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.paircoef: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + f.write('\n') #allows space to be added between the end of body data and a new keyword + elif row=='Bond Coeffs': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.bondcoef: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + f.write('\n') #allows space to be added between the end of body data and a new keyword + elif row=='Angle Coeffs': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.anglecoef: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + f.write('\n') #allows space to be added between the end of body data and a new keyword + elif row=='Dihedral Coeffs': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.dihedralcoef: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + f.write('\n') #allows space to be added between the end of body data and a new keyword + elif row=='Improper Coeffs': + f.write('\n{0}'.format(row)) #new line between header and body portion or two body keywords + f.write('\n') #new line between body keyword and body data + for line in self.impropercoef: + f.write('\n') #creates a new line for adding body data + for item in line: + f.write(' {0}'.format(item)) #adds in each peice of body data with space imbetween + f.write('\n') #allows space to be added between the end of body data and a new keyword + else: + pass + f.close() + + def atomorder(self): + """Takes self.atoms and organizes the atom id from least to greatest. + If the atom ids are allready ordered this algorithm will do nothing.""" + current=range(len(self.atoms[0])) # initialize current [assumes self.atoms coloumn #'s does not change] + for i in range(1,len(self.atoms)): #when i=0, self.atoms will not change; therefore its skipped + for k in range(len(self.atoms[i])): + current[k]=self.atoms[i][k] + for j in range(i-1,-1,-1): + for k in range(len(self.atoms[j])): + self.atoms[j+1][k]=self.atoms[j][k] + if int(current[0]) > int(self.atoms[j][0]): + for k in range(len(current)): + self.atoms[j+1][k]=current[k] + break + elif j==0: + for k in range(len(current)): + self.atoms[j][k]=current[k] + #dont need a break here because this is the last j value in the for loop + + def addatoms(self, atoms, retlist=False): + """Appends atoms to self.atoms. Assumes atoms are written in the correct atomtype format. + Change added atom numbers in self.atoms so self.atoms will be in increasing sequential order. + If retlist is True return a list of the modified atom numbers otherwise exit.""" + initpos=len(self.atoms) #Store index of first appended atoms + for item in atoms: + self.atoms.append(range(len(item))) #initializing spots for the new atoms to go + numberchange=[] #initiate number change where the changed atom numbers will be stored + count=0 + for i in range(initpos,len(self.atoms)): + for j in range(len(self.atoms[i])): + if j==0: + self.atoms[i][0]=str(i+1) + numberchange.append(str(i+1)) + else: + self.atoms[i][j]=atoms[count][j] + count+=1 + if retlist: return numberchange + else: return #need to redo this algorithm so their is no referencing going on. + + def adddata(self,data,keyword): + """Adds data to a keyword's existing data structure + All body keywords are viable except Atoms which has its own method + All header keywords will do nothing in this algrorithm because they have their own algorithm + changes the body id number so id numbers will be in sequential order""" + if keyword=='Velocities': + pos=len(self.velocities) + for item in data: + self.velocities.append(item) #adds data to existing data structure + self.velocities[pos][0]=str(pos+1) #changing body id number + pos+=1 + elif keyword=='Masses': + pos=len(self.masses) + for item in data: + self.masses.append(item) #adds data to existing data structure + self.masses[pos][0]=str(pos+1) #changing body id number + pos+=1 + elif keyword=='Shapes': + pos=len(self.shapes) + for item in data: + self.shapes.append(item) #adds data to existing data structure + self.shapes[pos][0]=str(pos+1) #changing body id number + pos+=1 + elif keyword=='Dipoles': + pos=len(self.dipoles) + for item in data: + self.dipoles.append(item) #adds data to existing data structure + self.dipoles[pos][0]=str(pos+1) #changing body id number + pos+=1 + elif keyword=='Bonds': + pos=len(self.bonds) + for item in data: + self.bonds.append(item) #adds data to existing data structure + self.bonds[pos][0]=str(pos+1) + pos+=1 + elif keyword=='Angles': + pos=len(self.angles) + for item in data: + self.angles.append(item) #adds data to existing data structure + self.angles[pos][0]=str(pos+1) #changing body id number + pos+=1 + elif keyword=='Dihedrals': + pos=len(self.dihedrals) + for item in data: + self.dihedrals.append(item) #adds data to existing data structure + self.dihedrals[pos][0]=str(pos+1) #changing body id number + pos+=1 + elif keyword=='Impropers': + pos=len(self.impropers) + for item in data: + self.impropers.append(item) #adds data to existing data structure + self.impropers[pos][0]=str(pos+1) #changing body id number + pos+=1 + elif keyword=='Pair Coeffs': + pos=len(self.paircoef) + for item in data: + self.paircoef.append(item) #adds data to existing data structure + self.paircoef[pos][0]=str(pos+1) #changing body id number + pos+=1 + elif keyword=='Bond Coeffs': + pos=len(self.bondcoef) + for item in data: + self.bondcoef.append(item) #adds data to existing data structure + self.bondcoef[pos][0]=str(pos+1) #changing body id number + pos+=1 + elif keyword=='Angle Coeffs': + pos=len(self.anglecoef) + for item in data: + self.anglecoef.append(item) #adds data to existing data structure + self.anglecoef[pos][0]=str(pos+1) #changing body id number + pos+=1 + elif keyword=='Dihedral Coeffs': + pos=len(self.dihedralcoef) + for item in data: + self.dihedralcoef.append(item) #adds data to existing data structure + self.dihedralcoef[pos][0]=str(pos+1) #changing body id number + pos+=1 + elif keyword=='Improper Coeffs': + pos=len(self.impropercoef) + for item in data: + self.impropercoef.append(item) #adds data to existing data structure + self.impropercoef[pos][0]=str(pos+1) #changing body id number + pos+=1 + +# def modifiydata(data,keyword): Will not implement +# """for modifying header data""" + + def changeatomnum(self,data,originalatoms,atomchanges,vflag=False): + """Takes data which contains atom ids + and alters those ids from the original atom id system in originalatoms + to the new atom id system in atomchanges.""" + #set up boolean array to match the size of data and with all True values +# print atomchanges + array=booleanarray(len(data),len(data[0]),True) +# print originalatoms + if vflag: # for changing atomnumbers for velocities + for i in range(len(originalatoms)): #len of originalatoms should match len of atomchanges + if originalatoms[i][0]==atomchanges[i]: continue + for j in range(len(data)): + for k in range(1): + if data[j][k]==originalatoms[i][0]: #checks if the atom id in data + #matches the atom id in origianalatoms + if array.getelement(j,k): #if the boolean array is true + #set the boolean array to False and + #change the atom id in data to atomchanges + array.setelement(j,k,False) + data[j][k]=atomchanges[i] + break + #the change of boolean array to False insures + #the atom id in data will only be changed once. + else: # for changing atom numbers for everything else + for i in range(len(originalatoms)): #len of originalatoms should match len of atomchanges + if originalatoms[i][0]==atomchanges[i]: continue + for j in range(len(data)): + for k in range(2,len(data[j])): + if data[j][k]==originalatoms[i][0]: #checks if the atom id in data + #matches the atom id in origianalatoms + if array.getelement(j,k): #if the boolean array is true + #set the boolean array to False and + #change the atom id in data to atomchanges + array.setelement(j,k,False) + data[j][k]=atomchanges[i] + break + #the change of boolean array to False insures + #the atom id in data will only be changed once. +# print data + return data + + def deletebodydata(self,keyword): + """Changes a keyword's class data structure to an empty structure""" + if keyword=='Velocities': + self.velocities=[] + elif keyword=='Masses': + self.masses=[] + elif keyword=='Shapes': + self.shapes=[] + elif keyword=='Dipoles': + self.dipoles=[] + elif keyword=='Bonds': + self.bonds=[] + elif keyword=='Angles': + self.angles=[] + elif keyword=='Dihedrals': + self.dihedrals=[] + elif keyword=='Impropers': + self.impropers=[] + elif keyword=='Pair Coeffs': + self.paircoef=[] + elif keyword=='Bond Coeffs': + self.bondcoef=[] + elif keyword=='Angle Coeffs': + self.anglecoef=[] + elif keyword=='Dihedral Coeffs': + self.dihedralcoef=[] + elif keyword=='Improper Coeffs': + self.impropercoef=[] + elif keyword=='Atoms': + self.atoms=[] + + def extractmolecules(self,molecule): + """Takes the variable molecule and + extracts the individual molecules' data back into lmpsdata. + This extraction takes place through a 4 step process. + Step 1: Use a molecule's keywords to alter the lmpsdata data structures to empty. + To acomplish this procedure use the lmpsdata method deletebodydata. + Step 2: Add the molecules' atoms to lmpsdata's atoms using the method addatoms. + Return a list of atom id changes for each molecule. + Step 3: Utilize each molecules list of atom id changes to change their data's atom id numbers. + Uses changeatomnum and returns the altered molecule's data. + Step 4: Add the altered molecules' data to lmpsdata's data using the method adddata""" + #Use molecule index 0's keyword to change the equivalent lmpsdata structures to empty + print 'extracting the molecules back to data' + for keyword in molecule[0].keywords: # step 1 + self.deletebodydata(keyword) + atomchanges=[] #initializing step 2 + for i in range(len(molecule)): # step 2 + atomchanges.append(self.addatoms(molecule[i].atoms,True)) + for i in range(len(molecule)): # step 3 + for keyword in molecule[i].keywords: + if keyword=='Angles': + molecule[i].angles=self.changeatomnum(molecule[i].angles,molecule[i].atoms,atomchanges[i]) + if keyword=='Bonds': + molecule[i].bonds=self.changeatomnum(molecule[i].bonds,molecule[i].atoms,atomchanges[i]) + elif keyword=='Dihedrals': + molecule[i].dihedrals=self.changeatomnum(molecule[i].dihedrals,molecule[i].atoms,atomchanges[i]) + elif keyword=='Velocities': + molecule[i].velocities=self.changeatomnum(molecule[i].velocities,molecule[i].atoms,atomchanges[i],True) + elif keyword=='Impropers': + molecule[i].impropers=self.changeatomnum(molecule[i].impropers,molecule[i].atoms,atomchanges[i]) + for i in range(len(molecule)): # step 4 + for keyword in molecule[i].keywords: + if keyword=='Angles': + self.adddata(molecule[i].angles,keyword) + elif keyword=='Bonds': + self.adddata(molecule[i].bonds,keyword) + elif keyword=='Dihedrals': + self.adddata(molecule[i].dihedrals,keyword) + elif keyword=='Velocities': + self.adddata(molecule[i].velocities,keyword) + elif keyword=='Impropers': + self.adddata(molecule[i].impropers,keyword) + + def density(self,ringsize,init,final,file=' '): + """Create spherical shells from the initial radius to the final radius + The spherical shell's thickness is defined by ringsize + Calculate the mass of particles within each spherical shell + Calculate the volume of each spherical shell + Then calculate the density in each spherical shell + Current code is written with the assumption the spheres are centered around the origin + If a file is listed place the results in the variable file otherwise return the results + The distance calculations are written assuming image flags are in the data file""" + rho=[] + r=[init] #initial radius to examine + radius=init+ringsize + while radius<final: #finding the rest of the radii to examine + r.append(radius) + radius+=ringsize + for radius in r: + volume=4.0/3.0*3.1416*((radius+ringsize)**3 - radius**3) + mass=0.0 + for row in self.atoms: + if self.atomtype=='angle' or self.atomtype=='atomic' or self.atomtype=='bond' or\ + self.atomtype=='charge' or self.atomtype=='colloid' or self.atomtype=='electron' or\ + self.atomtype=='full' or self.atomtype=='granular' or self.atomtype=='molecular' or\ + self.atomtype=='peri': + l=len(row)-1 + dist=float(row[l-3])**2+float(row[l-4])**2+float(row[l-5])**2 + if dist<(radius+ringsize)**2 and dist>=radius**2: + if self.atomtype=='atomic' or self.atomtype=='charge' or\ + self.atomtype=='colloid' or self.atomtype=='electron' or\ + self.atomtype=='granular' or self.atomtype=='peri': + type=int(row[1])-1 + else: + type=int(row[2])-1 + mass+=float(self.masses[type][1]) + #assumes self.masses is written in atomtype order + elif self.atomtype=='dipole': + l=len(row)-1 + dist=float(row[l-6])**2+float(row[l-7])**2+float(row[l-8])**2 + if dist<(radius+ringsize)**2 and dist>=radius**2: + type=int(row[1])-1 + mass+=float(self.masses[type][1]) + #assumes self.masses is written in atomtype order + elif self.atomtype=='ellipsoid': + l=len(row)-1 + dist=float(row[l-7])**2+float(row[l-8])**2+float(row[l-9])**2 + if dist<(radius+ringsize)**2 and dist>=radius**2: + type=int(row[1]) + mass+=float(self.masses[type][1]) + #assumes self.masses is written in atomtype order + elif self.atomtype=='hybrid': + dist=float(row[2])**2+float(row[3])**2+float(row[4])**2 + if dist<(radius+ringsize)**2 and dist>=radius**2: + type=int(row[1]) + mass+=float(self.masses[type][1]) + #assumes self.masses is written in atomtype order + rho.append(mass/volume) + if file==' ': + return r,rho + else: + f=open(file,'w') + for i in range(len(r)): + f.write('{0} {1}\n'.format(r[i],rho[i])) + f.close() + return + + def createxyz(self,file, routine='mass', values=None): + """Two possible routines one to use the masses from data and the other to use the atom type and values supplied by the user. + The mass version is assessed by setting the routine to 'mass' which is the default method. + The other version is assessed by setting the routine to 'atomtype'. + The other version takes values which is a list containing the value the user wants to assign those atomtypes to. + The atomtypes of the values in the list will start at 1 even if no atoms in molecule use 1. + This makes it easier to find the atomtype and assign the value from the list + All atom data is assumed to have image flags in the data.""" + f=open(file,'w') + f.write('{0}\n'.format(len(self.atoms))) + f.write('atoms\n') + if routine=='mass': + for line in self.atoms: + if self.atomtype=='angle' or self.atomtype=='bond' or self.atomtype=='full' or\ + self.atomtype=='molecular': + type=int(line[2])-1 #3rd position + else: + type=int(line[1])-1 #2nd position + mass=self.masses[type][1] + if mass=='12.0107': elementnum=6 + elif mass=='15.9994': elementnum=8 + elif mass=='26.9815':elementnum=13 + else: + print 'no matching mass value. The method will exit' + return + l=len(line)-1 + if self.atomtype=='angle' or self.atomtype=='atomic' or self.atomtype=='bond' or\ + self.atomtype=='charge' or self.atomtype=='colloid' or self.atomtype=='electron' or\ + self.atomtype=='full' or self.atomtype=='granular' or self.atomtype=='molecular' or\ + self.atomtype=='peri': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-5],line[l-4],line[l-3])) + elif self.atomtype=='dipole': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-8],line[l-7],line[l-6])) + elif self.atomtype=='ellipsoid': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-9],line[l-8],line[l-7])) + elif self.atomtype=='hybrid': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[2],line[3],line[4])) + f.close() + elif routine=='atomtype': + for line in self.atoms: + if self.atomtype=='angle' or self.atomtype=='bond' or self.atomtype=='full' or\ + self.atomtype=='molecular': + type=int(line[2])-1 #3rd position + else: + type=int(line[1])-1 #2nd position + elementnum=values[type] + l=len(line)-1 + if self.atomtype=='angle' or self.atomtype=='atomic' or self.atomtype=='bond' or\ + self.atomtype=='charge' or self.atomtype=='colloid' or self.atomtype=='electron' or\ + self.atomtype=='full' or self.atomtype=='granular' or self.atomtype=='molecular' or\ + self.atomtype=='peri': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-5],line[l-4],line[l-3])) + elif self.atomtype=='dipole': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-8],line[l-7],line[l-6])) + elif self.atomtype=='ellipsoid': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-9],line[l-8],line[l-7])) + elif self.atomtype=='hybrid': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[2],line[3],line[4])) + f.close() + + +class booleanarray: + """A class that stores boolean values in a list of lists.""" + def __init__(self,rownum, colnum, initval): + """ initialise a list of lists (array) with + rownum correspondinig to the number of lists in the list and + colnum corresponding to the number of elements in the list's list. + initval is the value the list of lists will be initialized with. + initval should be a boolean value.""" + # initializing self.array + self.array=[] + for i in range(rownum): + self.array.append(range(colnum)) + # setting elements of self.array to initval + for i in range(rownum): + for j in range(colnum): + self.setelement(i,j,initval) + + def setelement(self,rownum, colnum, value): + """Assigns value to the list of lists (array) element at rownum and colnum.""" + self.array[rownum][colnum]=value + + def getelement(self,rownum,colnum): + """Returns element in the list of lists (array) at rownum and colnum.""" + return self.array[rownum][colnum] + +def atomdistance(a,b,atomtype): + """Returns the distance between atom a and b. + Atomtype is the style the atom data is written in. + All atom data is assumed to have image flags in the data. + Atom a and atom b are assumed to be the same atomtype.""" + from math import sqrt + if atomtype=='angle' or atomtype=='atomic' or atomtype=='bond' or\ + atomtype=='charge' or atomtype=='colloid' or atomtype=='electron' or\ + atomtype=='full' or atomtype=='granular' or atomtype=='molecular' or\ + atomtype=='peri': + l=len(a)-1 + dist=sqrt((float(a[l-3])-float(b[l-3]))**2\ + +(float(a[l-4])-float(b[l-4]))**2\ + +(float(a[l-5])-float(b[l-5]))**2) + elif atomtype=='dipole': + l=len(a)-1 + dist=sqrt((float(a[l-6])-float(b[l-6]))**2\ + +(float(a[l-7])-float(b[l-7]))**2\ + +(float(a[l-8])-float(b[l-8]))**2) + elif atomtype=='ellipsoid': + l=len(a)-1 + dist=sqrt((float(a[l-7])-float(b[l-7]))**2\ + +(float(a[l-8])-float(b[l-8]))**2\ + +(float(a[l-9])-float(b[l-9]))**2) + elif atomtype=='hybrid': + dist=sqrt((float(a[2])-float(b[2]))**2\ + +(float(a[3])-float(b[3]))**2\ + +(float(a[4])-float(b[4]))**2) + return dist + +def distance(a,coord,atomtype): + """Returns the distance between atom a and coord. + Atomtype is the style the atom data is written in. + All atom data is assumed to have image flags in the data.""" + from math import sqrt #need to alter this slightly + if atomtype=='angle' or atomtype=='atomic' or atomtype=='bond' or\ + atomtype=='charge' or atomtype=='colloid' or atomtype=='electron' or\ + atomtype=='full' or atomtype=='granular' or atomtype=='molecular' or\ + atomtype=='peri': + l=len(a)-1 + dist=sqrt((float(a[l-3])-coord[2])**2\ + +(float(a[l-4])-coord[1])**2\ + +(float(a[l-5])-coord[0])**2) + elif atomtype=='dipole': + l=len(a)-1 + dist=sqrt((float(a[l-6])-coord[2])**2\ + +(float(a[l-7])-coord[1])**2\ + +(float(a[l-8])-coord[0])**2) + elif atomtype=='ellipsoid': + l=len(a)-1 + dist=sqrt((float(a[l-7])-coord[2])**2\ + +(float(a[l-8])-coord[1])**2\ + +(float(a[l-9])-coord[0])**2) + elif atomtype=='hybrid': + dist=sqrt((float(a[2])-coord[0])**2\ + +(float(a[3])-coord[1])**2\ + +(float(a[4])-coord[0])**2) + return dist + + +class particlesurface: + def __init__(self,particle,cutoff,atomid,atomtype,shape='sphere'): + """Builds a particle surface with a specific shape from a particle + The atoms choosen from the surface will have the specific atomid + atomid will be given in terms of an integer and not a string.""" + self.particle=particle.atoms + self.atomtype=atomtype + self.cutoff=cutoff + self.surface=[] + self.particlelocator=[] #stores surface particle locations with respect to the particle + self.deletedatoms=[] + if shape=='sphere': self.createspheresurf(atomid) + + def createspheresurf(self,atomid): + """Assumes sphere is centered around 0,0,0. + Finds maximum distance particle with atomid. + Than all atoms within cutoff distance of max distance + will be included into self.surface + Also will build a list of where the surface particles are + in relationship to the particle. + Will create a booleanarray to keep track of any changes made to the surface.""" + particledist=[] + for row in self.particle: #Stores all of the distances of the particle in particledist + if self.atomtype=='angle' or self.atomtype=='bond' or self.atomtype=='full' or\ + self.atomtype=='molecular': + type=int(row[2]) #3rd position + else: + type=int(row[1]) #2nd position + if type==atomid: #only calculates the distance if the atom has the correct atomid number + particledist.append(distance(row,[0,0,0],self.atomtype)) + else: + particledist.append(0.0) + self.maxdist=max(particledist)# finds the max dist. + for i in range(len(particledist)): + if particledist[i]>=(self.maxdist-self.cutoff): + self.particlelocator.append(i) + self.surface.append(self.particle[i]) + self.bool=booleanarray(len(self.surface),1,True) + + def getsurfatom(self,rownum): + return self.surface[rownum] + + def getbool(self,rownum): + return self.bool.getelement(rownum,0) + + def setbool(self,rownum,value): + self.bool.setelement(rownum,0,value) + + def removesurfatom(self,rownum): + """note this does not actually remove the surface atom. + Instead this adds a value to the list deletedatoms. + This list is later used in extractparticle to actually delete those atoms""" + self.deletedatoms.append(self.particlelocator[rownum]) + + def extractparticle(self): + """"deletesatoms from particle from the list of deletedatoms + and than returns the particle.""" + self.particle=self.deleteatoms(self.particle,self.deletedatoms) + return self.particle + + def deleteatoms(self,structure,rows): + """delete atoms from particle and shifts the structure down""" + new=[] + #mulitple copying of b to the rows being replaced. + if rows==[]: + for line in structure: + new.append(line) #if no rows need replacing copy structure + return new + for i in range(rows[0]): + new.append(structure[i]) #copy structure to new until the first replaced row + count=0 + for i in range(rows[0],len(structure)-len(rows)):# to replace rows and shift undeleted rows over + for j in range(i+1+count,len(structure)): + for val in rows: + if val==j: + count+=1 + break + if val==j:continue + else: + new.append(structure[j]) + break + return new + + def addatom(self,atomtype,charge=None,moleculenum=None): + """Adds an atom to the particle surface between maxdist and the cutoff distance. + This atom is stored in self.particle and self.surface. + In the current coding the particle is centered at (0,0,0). + This method has a required input of atomtype and optional inputs of charge and moleculenum. + This method currently does not support correctly self.atomtype of dipole, electron, ellipsoid, granular, peri or hybrid + Will use the image flags 0,0,0. + Note: The atomtype here is different from the atomtype attribute as part of this class + Note: If the added atom will be required for bonding later than you will need to extract the particle data + and rebuild the surface, because this method doesn't include updates to the required variables due to some coding issues""" + import math, random + # Checks to make sure self.atomtype is not set to an unsupported value + if self.atomtype=='dipole' or self.atomtype=='electron' or\ + self.atomtype=='ellipsoid' or self.atomtype=='peri' or self.atomtype=='hybrid': + print self.atomtype, 'is not currently supported. But, you can add support by adding the needed functionality and inputs' + return + + print 'adding an atom to the surface' + # Randomly assigns the position of a new atom in a spherical shell between self.cutoff and self.maxdist + foundpoint=False + while (foundpoint==False): + x=random.uniform(-self.maxdist,self.maxdist) + y=random.uniform(-self.maxdist,self.maxdist) + try: + z=random.uniform(math.sqrt(self.cutoff**2-x**2-y**2),math.sqrt(self.maxdist**2-x**2-y**2)) + except ValueError: + continue + distance=math.sqrt(x**2+y**2+z**2) + if distance<self.maxdist and distance>=self.maxdist-self.cutoff: + foundpoint=True + + # initialize new row in self.particle and self.surface + self.particle.append([]) + self.surface.append([]) + + pposition=len(self.particle)-1 #particle postion + sposition=len(self.surface)-1 #surface position + + # Adds the atom id number to the new row + self.particle[pposition].append(str(pposition+1)) + self.surface[sposition].append(str(pposition+1)) + + # Adds the atomtype and the moleculenum if needed to the new row + if self.atomtype=='angle' or self.atomtype=='bond' or self.atomtype=='full' or\ + self.atomtype=='molecular': + self.particle[pposition].append(str(moleculenum)) + self.surface[sposition].append(str(moleculenum)) + self.particle[pposition].append(str(atomtype)) + self.surface[sposition].append(str(atomtype)) + else: + self.particle[pposition].append(str(atomtype)) + self.surface[sposition].append(str(atomtype)) + + # Adds the charge if needed to the new row + if self.atomtype=='charge' or self.atomtype=='full': + self.particle[pposition].append(str(charge)) + self.surface[sposition].append(str(charge)) + + # Adds the atom's position to the new row + self.particle[pposition].append(str(x)) + self.surface[sposition].append(str(x)) + self.particle[pposition].append(str(y)) + self.surface[sposition].append(str(y)) + self.particle[pposition].append(str(z)) + self.surface[sposition].append(str(z)) + + # Adds the atom's image flags to the new row + self.particle[pposition].append('0') + self.surface[sposition].append('0') + self.particle[pposition].append('0') + self.surface[sposition].append('0') + self.particle[pposition].append('0') + self.surface[sposition].append('0') + + + def createxyz(self,file,data,routine='mass', values=None): + """This shows the particle surface. To show the particle surface after bonding has occured, + you will need to extract the particle than reinsert the particle into the class and use createxyz. + Two possible routines one to use the masses from data and the other to use the atom type and values supplied by the user. + The mass version is assessed by setting the routine to 'mass' which is the default method. + The other version is assessed by setting the routine to 'atomtype'. + The other version takes values which is a list containing the value the user wants to assign those atomtypes to. + The atomtypes of the values in the list will start at 1 even if no atoms in molecule use 1. + This makes it easier to find the atomtype and assign the value from the list + All atom data is assumed to have image flags in the data.""" + f=open(file,'w') + f.write('{0}\n'.format(len(self.surface))) + f.write('atoms\n') + if routine=='mass': + for line in self.surface: + if self.atomtype=='angle' or self.atomtype=='bond' or self.atomtype=='full' or\ + self.atomtype=='molecular': + type=int(line[2])-1 #3rd position + else: + type=int(line[1])-1 #2nd position + mass=data.masses[type][1] + if mass=='12.0107': elementnum=6 + elif mass=='15.9994': elementnum=8 + elif mass=='26.981539':elementnum=13 + else: + print 'no matching mass value. The method will exit' + return + l=len(line)-1 + if self.atomtype=='angle' or self.atomtype=='atomic' or self.atomtype=='bond' or\ + self.atomtype=='charge' or self.atomtype=='colloid' or self.atomtype=='electron' or\ + self.atomtype=='full' or self.atomtype=='granular' or self.atomtype=='molecular' or\ + self.atomtype=='peri': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-5],line[l-4],line[l-3])) + elif self.atomtype=='dipole': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-8],line[l-7],line[l-6])) + elif self.atomtype=='ellipsoid': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-9],line[l-8],line[l-7])) + elif self.atomtype=='hybrid': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[2],line[3],line[4])) + f.close() + elif routine=='atomtype': + for line in self.surface: + if self.atomtype=='angle' or self.atomtype=='bond' or self.atomtype=='full' or\ + self.atomtype=='molecular': + type=int(line[2])-1 #3rd position + else: + type=int(line[1])-1 #2nd position + elementnum=values[type] + l=len(line)-1 + if self.atomtype=='angle' or self.atomtype=='atomic' or self.atomtype=='bond' or\ + self.atomtype=='charge' or self.atomtype=='colloid' or self.atomtype=='electron' or\ + self.atomtype=='full' or self.atomtype=='granular' or self.atomtype=='molecular' or\ + self.atomtype=='peri': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-5],line[l-4],line[l-3])) + elif self.atomtype=='dipole': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-8],line[l-7],line[l-6])) + elif self.atomtype=='ellipsoid': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-9],line[l-8],line[l-7])) + elif self.atomtype=='hybrid': + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[2],line[3],line[4])) + f.close() + +def molecules(data,init,final,processors, method='all'): + """ There are two ways to initialize the class Lmpsmolecule. + Method controls which way is chosen. + Acceptable values for Method are 'all', 'atom'. + The default method is 'all'""" + molecule=[] + from multiprocessing import Pool + p=Pool(processors) + for i in range(init,final+1): + molecule.append(p.apply_async(Lmpsmolecule,(i,data,method,))) + for i in range(len(molecule)): + molecule[i]=molecule[i].get() + p.close() + p.join() + return molecule + +class Lmpsmolecule: #Technically should be a meta class but written as a seperate class for easier coding. + def __init__(self,moleculenum,data,method): + """initiates lammps molecule structures + and than extract the appropriate molecular structures from the base class data""" +# ***** Lammps Molecule Structures + self.keywords=['Atoms']#include atoms here since this will be in every instance of this class + self.atoms=[] #extract + self.angles=[] #extract if keyword is there + self.bonds=[] #extract if keyword is there + self.dihedrals=[] #extract if keyword is there + self.impropers=[] #extract if keyword is there + self.velocities=[] #extract if keyword is there +# ***** Molecule number + self.moleculenum=moleculenum +# ***** Extracting the moleculue's atom information from data + self.extract(data,method) +# ***** If methods is 'all' then self.extract will extract all of the molecule's information from data + + def extract(self,data,method): + """uses base class data to extract the molecules atoms + and any other molecule data from molecule moleculenum. This extraction is done in a two step process. + Step 1: extract data.atoms with moleculenum and renumber self.atoms beginning from 1 + store extracted atom numbers from data.atoms in a list called temp + Step 2: pass temp and data to method changeatomnum + which extracts the rest of the Lammps Molecule structures from data + and changes the atomnumbers in those structures to match the ones already in self.atoms""" + #checking to make sure atomtype is a valid molecule + #if not a valid molecule print error and exit method + if data.atomtype=='full' or data.atomtype=='molecular': +# ***** Sets the molecule's atomtype + self.atomtype=data.atomtype + else: + print "not a valid molecular structure" + return + #extract the molecule self.moleculenum from data.atom to self.atom + atomnum=1 + temp=[] + for i in range(len(data.atoms)): + if int(data.atoms[i][1])==self.moleculenum: + temp.append(data.atoms[i][0]) #store extracted atomnumbers into temp + self.atoms.append(data.atoms[i]) #store extracted atom into self.atom + self.atoms[atomnum-1][0]=str(atomnum) #change extracted atom to the correct atomnumber + atomnum+=1 #update atomnumber + #extract the rest of the Lammps Molecule structures from data using changeatomnum + if method=='atom': + return + else: + self.changeatomnum(temp,data) + + def changeatomnum(self,temp,data=' '): + """changes the atomnumbers in Lmpsmolecule structures angles, bonds, dihedrals, impropers, + and velocities in a three step process. + If data is defined extract the rest of the keywords used for Lmpsmolecule. + Step 1A:If data is defined copy from data one of the above structures. + If data is not defined skip this step. + Step 1B:If data is defined remove the rows of copy which do not contain any values from temp + Step 2: If data is defined extract from copy to an above Lmpsmolecule structure and remove the extracted rows + If data is not defined skip this step. + Step 3: Use temp and self.atoms to convert the atomnumbers in Lmpsmolecule structures + to the correct values. + temp and self.atoms line up, so temp[i] and self.atoms[i][0] + correspond to the current Lmpsmolecule structures atom numbers + and correct values + will use booleanarray class to ensure that lmpsmolecule's structure values are altered only once.""" + #Step 1 and Step 2 + if data!=' ': + #extract molecular keywords from data.keywords + for keywords in data.keywords: + if keywords=='Angles' or keywords=='Bonds' or keywords=='Dihedrals' or\ + keywords=='Impropers' or keywords=='Velocities': + self.keywords.append(keywords) + for keywords in self.keywords: + if keywords!='Atoms': print 'extracting the data from', keywords + if keywords=='Angles': + copy=self.copy(data.angles) #Step 1A + dnr=[] #dnr means do not remove a 0 means remove and a 1 means keep + for j in range(len(copy)): #Step 1B + dnr.append(0) #adds 0 to all list elements of dnr + for item in temp: #finds copied structure that has temp values + for j in range(len(copy)): + for i in range(2,len(copy[j])): + if copy[j][i]==item: + dnr[j]=1 #changes jth list element of dnr to 1 + break + remove=[] + for j in range(len(dnr)): # finds dnr values that are still 0 + if dnr[j]==0: + remove.append(j) #and appends their index value to the list remove + copy=self.deleterows(copy,remove) #removes all unneeded rows from copy + structnum=1 + print 'the length of data is', len(copy) + for item in temp: #Step 2 + found=[] + for j in range(len(copy)): + for i in range(2,len(copy[j])): + if copy[j][i]==item: + found.append(j) + self.angles.append(copy[j]) + l=len(self.angles)-1 + self.angles[l][0]=str(structnum) + structnum+=1 + break + # print 'the item is', item + # print 'found is', found + copy=self.deleterows(copy,found) + elif keywords=='Bonds': + copy=self.copy(data.bonds) #Step 1A + dnr=[] #dnr means do not remove a 0 means remove and a 1 means keep + for j in range(len(copy)): #Step 1B + dnr.append(0) #adds 0 to all list elements of dnr + for item in temp: #finds copied structure that has temp values + for j in range(len(copy)): + for i in range(2,len(copy[j])): + if copy[j][i]==item: + dnr[j]=1 #changes jth list element of dnr to 1 + break + remove=[] + for j in range(len(dnr)): # finds dnr values that are still 0 + if dnr[j]==0: + remove.append(j) #and appends their index value to the list remove + copy=self.deleterows(copy,remove) #removes all unneeded rows from copy + structnum=1 + print 'the length of data is', len(copy) + for item in temp: #Step 2 + found=[] + for j in range(len(copy)): + for i in range(2,len(copy[j])): + if copy[j][i]==item: + found.append(j) + self.bonds.append(copy[j]) + l=len(self.bonds)-1 + self.bonds[l][0]=str(structnum) + structnum+=1 + break + # print 'the item is', item + # print 'found is', found + copy=self.deleterows(copy,found) + elif keywords=='Dihedrals': + copy=self.copy(data.dihedrals) #Step 1A + dnr=[] #dnr means do not remove a 0 means remove and a 1 means keep + for j in range(len(copy)): #Step 1B + dnr.append(0) #adds 0 to all list elements of dnr + for item in temp: #finds copied structure that has temp values + for j in range(len(copy)): + for i in range(2,len(copy[j])): + if copy[j][i]==item: + dnr[j]=1 #changes jth list element of dnr to 1 + break + remove=[] + for j in range(len(dnr)): # finds dnr values that are still 0 + if dnr[j]==0: + remove.append(j) #and appends their index value to the list remove + copy=self.deleterows(copy,remove) #removes all unneeded rows from copy + structnum=1 + print 'the length of data is', len(copy) + structnum=1 + for item in temp: #Step 2 + found=[] + for j in range(len(copy)): + for i in range(2,len(copy[j])): + if copy[j][i]==item: + found.append(j) + self.dihedrals.append(copy[j]) + l=len(self.dihedrals)-1 + self.dihedrals[l][0]=str(structnum) + structnum+=1 + break + # print 'the item is', item + # print 'found is', found + copy=self.deleterows(copy,found) + elif keywords=='Impropers': + copy=self.copy(data.impropers) #Step 1B + dnr=[] #dnr means do not remove a 0 means remove and a 1 means keep + for j in range(len(copy)): #Step 1B + dnr.append(0) #adds 0 to all list elements of dnr + for item in temp: #finds copied structure that has temp values + for j in range(len(copy)): + for i in range(2,len(copy[j])): + if copy[j][i]==item: + dnr[j]=1 #changes jth list element of dnr to 1 + break + remove=[] + for j in range(len(dnr)): # finds dnr values that are still 0 + if dnr[j]==0: + remove.append(j) #and appends their index value to the list remove + copy=self.deleterows(copy,remove) #removes all unneeded rows from copy + structnum=1 + print 'the length of data is', len(copy) + for item in temp: #Step 2 + found=[] + for j in range(len(copy)): + for i in range(2,len(copy[j])): + if copy[j][i]==item: + found.append(j) + self.impropers.append(copy[j]) + l=len(self.impropers)-1 + self.impropers[l][0]=str(structnum) + structnum+=1 + break + # print 'the item is', item + # print 'found is', found + copy=self.deleterows(copy,found) + elif keywords=='Velocities': + copy=self.copy(data.velocities) #Step 1 + dnr=[] #dnr means do not remove a 0 means remove and a 1 means keep + for j in range(len(copy)): #Step 1B + dnr.append(0) #adds 0 to all list elements of dnr + for item in temp: #finds copied structure that has temp values + for j in range(len(copy)): + for i in range(1): + if copy[j][i]==item: + dnr[j]=1 #changes jth list element of dnr to 1 + break + remove=[] + for j in range(len(dnr)): # finds dnr values that are still 0 + if dnr[j]==0: + remove.append(j) #and appends their index value to the list remove + copy=self.deleterows(copy,remove) #removes all unneeded rows from copy + structnum=1 + print 'the length of data is', len(copy) + for item in temp: #Step 2 + found=[] + for j in range(len(copy)): + for i in range(1): + if copy[j][i]==item: + found.append(j) + self.velocities.append(copy[j]) + l=len(self.velocities)-1 + self.velocities[l][0]=str(structnum) + structnum+=1 + break + # print 'the item is', item + # print 'found is', found + copy=self.deleterows(copy,found) + + #Step 3 + for keywords in self.keywords: + if keywords!='Atoms': print 'altering data structure values for', keywords + if keywords=='Angles': + copy=self.copy(self.angles) + array=booleanarray(len(copy),len(copy[0]),True) #creating a booleanarray with true values + for i in range(len(temp)): + if temp[i]==self.atoms[i][0]: continue + for j in range(len(copy)): + for k in range(2,len(copy[j])): + if copy[j][k]==temp[i]: + if array.getelement(j,k): #if the boolean array is true + self.angles[j][k]=self.atoms[i][0] + array.setelement(j,k,False) + break + elif keywords=='Bonds': + copy=self.copy(self.bonds) + array=booleanarray(len(copy),len(copy[0]),True) #creating a booleanarray with true values + for i in range(len(temp)): + if temp[i]==self.atoms[i][0]: continue + for j in range(len(copy)): + for k in range(2,len(copy[j])): + if copy[j][k]==temp[i]: + if array.getelement(j,k): #if the boolean array is true + self.bonds[j][k]=self.atoms[i][0] + array.setelement(j,k,False) + break + elif keywords=='Dihedrals': + copy=self.copy(self.dihedrals) + array=booleanarray(len(copy),len(copy[0]),True) #creating a booleanarray with true values + for i in range(len(temp)): + if temp[i]==self.atoms[i][0]: continue + for j in range(len(copy)): + for k in range(2,len(copy[j])): + if copy[j][k]==temp[i]: + if array.getelement(j,k): #if the boolean array is true + self.dihedrals[j][k]=self.atoms[i][0] + array.setelement(j,k,False) + break + elif keywords=='Impropers': + copy=self.copy(self.impropers) + array=booleanarray(len(copy),len(copy[0]),True) #creating a booleanarray with true values + for i in range(len(temp)): + if temp[i]==self.atoms[i][0]: continue + for j in range(len(copy)): + for k in range(2,len(copy[j])): + if copy[j][k]==temp[i]: + if array.getelement(j,k): #if the boolean array is true + self.impropers[j][k]=self.atoms[i][0] + array.setelement(j,k,False) + break + elif keywords=='Velocities': + copy=self.copy(self.velocities) + array=booleanarray(len(copy),len(copy[0]),True) #creating a booleanarray with true values + for i in range(len(temp)): + if temp[i]==self.atoms[i][0]: continue + for j in range(len(copy)): + for k in range(1): + if copy[j][k]==temp[i]: + if array.getelement(j,k): #if the boolean array is true + self.velocities[j][k]=self.atoms[i][0] + array.setelement(j,k,False) + break + + def copy(self,structure): + """copies structure to same and returns same.""" + same=[] + for row in structure: + same.append(row) + return same + + def deleterows(self,structure,rows): # run through this {structure is list of strings and rows is list + #of numbers} + """delete rows in a structure and shifts the structure up + rows must be in increasing order for this algorithm to work correctly""" + new=[] + #mulitple copying of b to the rows being replaced. + if rows==[]: + for line in structure: + new.append(line) #if no rows need replacing copy structure + return new + for i in range(rows[0]): + new.append(structure[i]) #copy structure to new until the first replaced row + count=0 + for i in range(rows[0],len(structure)-len(rows)):# to replace rows and shift undeleted rows over + for j in range(i+1+count,len(structure)): + for val in rows: + if val==j: + count+=1 + break + if val==j:continue + else: + new.append(structure[j]) + break + return new + + def modifyatom(self,atomnumber,column,newvalue): + """modifies self.atom[atomnumber-1][column] to newvalue. + *note: newvalue needs to be a string + if column is 0 than changeatomnum method might need runnning for all atoms + that have had their atomnumber(column=0) modified. + changeatomnum method is not ran in this method when column=0""" + self.atoms[atomnumber-1][column]=newvalue + + + def deleteatoms(self,atomnumbers,atomid): + """Algorithm to find all atoms bonded to atomnumbers in the direction of atomid. + Atomid can be a list or a single integer. The single integer corresponds to atom's atomtype value. + The list corresponds to the atom's atomid values. The only difference between both cases is in the top portion of code. + When all bonded atoms have been found; ie: (the modified return values from findbonds yields an empty list); + delete those bonded atoms and all molecule structures which contain those atoms. + Convert the list of bonded atoms into rows and delete those rows from molecule.atoms""" + print 'finding atoms to delete' + bondedatoms=[] + # 1st iteration + nextatoms=self.findbonds(atomnumbers) + try: # tests whether atomid is a list or a single integer + atomid[0] + except TypeError: + #need to remove atoms from nextatoms which dont have the proper atom id + testflag=True + i=0 + while testflag: + if i>len(nextatoms)-1: break #For the case where i becomes greater than the len of nextatoms break loop + if int(self.atoms[nextatoms[i]-1][2])!=atomid:#uses the atom id for the row in atoms and than checks atom id + del nextatoms[i] #delets the atom at i + i-=1 #and than decreases i by 1 so next atom in the list will line up when i is increased + i+=1 #increase i by 1 + else: + #need to remove atoms from nextatoms which dont have the proper atom id + testflag=True + i=0 + while testflag: + if i>len(nextatoms)-1: break #For the case where i becomes greater than the len of nextatoms break loop + keep=False + for id in atomid: + if int(self.atoms[nextatoms[i]-1][0])==id: #checking if atomid is in next atom + keep=True #keep this atom + break + if not keep: + del nextatoms[i] #delets the atom at i + i-=1 #and than decreases i by 1 so next atom in the list will line up when i is increased + i+=1 #increase i by 1 + + #append next atoms into bondedatoms + #copy next atoms into prevatoms + prevatoms=[] + for atom in nextatoms: + bondedatoms.append(atom) + prevatoms.append(atom) + + + #2nd iteration + if prevatoms==[]: + print 'no bonds were found in first iteration that had atomid criteria' + return + + nextatoms=self.findbonds(prevatoms) + #need to remove atoms from nextatoms which are in atomnumbers + for atom in atomnumbers: + for i in range(len(nextatoms)): + if nextatoms[i]==atom: #checking if atom is in next atom + del nextatoms[i] #delete the atom at i + break + if nextatoms==[]: break #all bonds from find bonds have allready been added to bondedatoms + + #append next atoms into bondedatoms + #copy next atoms into prevatoms + prevatoms=[] + for atom in nextatoms: + bondedatoms.append(atom) + prevatoms.append(atom) + + #iterative proccess for finding the rest of the atoms bonded to atomnumbers in the direction of atomid. + while prevatoms!=[]: + nextatoms=self.findbonds(prevatoms) + #need to remove atoms from nextatoms which are in the prevatoms + for atom in bondedatoms: + for i in range(len(nextatoms)): + if nextatoms[i]==atom: #checking if atom is in next atom + del nextatoms[i] #delete the atom at i + break + if nextatoms==[]: break #all bonds from find bonds have allready been added to bondedatoms + + #append next atoms into bondedatoms + #copy next atoms into prevatoms + prevatoms=[] + for atom in nextatoms: + bondedatoms.append(atom) + prevatoms.append(atom) + + print 'the atoms to delete are', bondedatoms + print 'deleting atoms from structures' + #delete bonded atoms from the molecule's structures except the atom structure + for i in range(1,len(self.keywords)): #goes through all keywords except the atom keyword + print self.keywords[i] + if self.keywords[i]=='Angles': + rows=self.findatomnumbers(bondedatoms,self.angles,False) + rows=self.listorder(rows) #to order the rows in increasing order + self.angles=self.deleterows(self.angles,rows) #requires that rows be in increasing order + elif self.keywords[i]=='Bonds': + rows=self.findatomnumbers(bondedatoms,self.bonds,False) + rows=self.listorder(rows) #to order the rows in increasing order + self.bonds=self.deleterows(self.bonds,rows)#requires that rows be in increasing order + elif self.keywords[i]=='Dihedrals': + rows=self.findatomnumbers(bondedatoms,self.dihedrals,False) + rows=self.listorder(rows) #to order the rows in increasing order + self.dihedrals=self.deleterows(self.dihedrals,rows) #requires that rows be in increasing order + elif self.keywords[i]=='Impropers': + rows=self.findatomnumbers(bondedatoms,self.impropers,False) + rows=self.listorder(rows) #to order the rows in increasing order + self.impropers=self.deleterows(self.impropers,rows) #requires that rows be in increasing order + elif self.keywords[i]=='Velocities': + rows=self.findatomnumbers(bondedatoms,self.velocities,True) + rows=self.listorder(rows) #to order the rows in increasing order + self.velocities=self.deleterows(self.velocities,rows) #requires that rows be in increasing order + + print 'Atoms' + #convert bondedatoms from atom numbers to row numbers + for i in range(len(bondedatoms)): + bondedatoms[i]-=1 + bondedatoms=self.listorder(bondedatoms) #to order the row numbers in increasing order + #delete bonded atoms (row numbers) from the atom structure + self.atoms=self.deleterows(self.atoms,bondedatoms) #requires that row numbers be in increasing order + + def findatomnumbers(self,atomnumbers,structure,vflag): #need to read through this algorithm + """Algorithm to find atomnumbers in a molecule structure except. + Atoms structure is not handled in here. + Returns a list of the rows in which the atomnumbers are contained in the molecule structure""" + rows=[] + if vflag: #for handling velocity structure + for atom in atomnumbers: + for i in range(len(structure)): + if int(structure[i][0])==atom: + #duplicate rows for vflag=True are not possible. + rows.append(i) + break + else: #for handling all other structures except atoms + for atom in atomnumbers: + for i in range(len(structure)): + for j in range(2,len(structure[i])): + if int(structure[i][j])==atom: + #need to make sure duplicate value of rows are not being added + if rows==[]: + rows.append(i) #appends the row number + else: + #checking for duplicate values of rows + duplicateflag=0 + for k in range(len(rows)): + if rows[k]==i: + duplicateflag=1 + break + if duplicateflag==0: #if no duplicates adds bond number + rows.append(i) #appends the row number + break + print 'the finished row is', rows + return rows + + def listorder(self,struct): + """Takes struct and organizes the list from least to greatest. + If the list is allready ordered this algorithm will do nothing.""" + if len(struct)==1: return struct #with the length at 1; thier is only one element and theirfore nothing to order + for i in range(1,len(struct)): #when i=0, struct will not change; therefore its skipped + copy=struct[i] + for j in range(i-1,-1,-1): + struct[j+1]=struct[j] + if copy >struct[j]: + struct[j+1]=copy + break + elif j==0: + struct[j]=copy + #dont need a break here because this is the last j value in the for loop + print 'the organized row is', struct + return struct + + + + def findbonds(self,atomnumbers): + """Algorithm to find all atoms bonded to atomnumbers. + Returns a list of atomnumbers""" + #finds the bonds in which the atomnumbers are located + bondids=[] + for i in range(len(atomnumbers)): + for j in range(len(self.bonds)): + for k in range(2,len(self.bonds[j])): + if int(self.bonds[j][k])==atomnumbers[i]: + if bondids==[]: + bondids.append(int(self.bonds[j][0])) #appends the bond number + else: + #checking for duplicates of bondids + duplicateflag=0 + for l in range(len(bondids)): + if bondids[l]==int(self.bonds[j][0]): + duplicateflag=1 + break + if duplicateflag==0: #if no duplicates adds bond number + bondids.append(int(self.bonds[j][0])) + break + + #Using bondids find the atoms bonded to atomnumbers + bondedatoms=[] + from math import fabs + for id in bondids: + for atoms in atomnumbers: + found=False + for i in range(2,len(self.bonds[id-1])): + if int(self.bonds[id-1][i])==atoms: + j=int(fabs(i-5)) #switches the index from the atomnumber location to the other location + bondedatoms.append(int(self.bonds[id-1][j])) #appends the atomnuber at the other location + found=True + break + if found==True: break + return bondedatoms + + def findparticlebondingpoints(self,particle,atomid,cutoffdistance,bondnumber): + """Particle is a particlesurfaceobject, atomid is an int. + Finds atoms with atomid in the molecule which are less than the cutoffdistance from atoms in the particle. + The found atoms and particles locations are stored in a 3 dimensional list called possiblebonds + The first index corresponds with the molecule's atom + The second index correspons with the molecule/particle combination + The third index corresponds with whether the value is the molecule or the particle + Always bonds the two ends of the molecule that meet cutoff requirement. + All other possible bonds are randomly choosen until the required bondnumbers are met. + After every bond is choosen, the particle object's boolean list is updated, and possiblebonds is updated. + The update to possiblebonds involves removing the row from which the bonded molecule's atom is located + and also removing the particle atom and it's corresponding bonded atom from other rows of possiblebonds. + The final bonds are all stored in self.bonding as a 2 dimensional list""" + possiblebonds=[] + for i in range(len(self.atoms)): + if int(self.atoms[i][2])==atomid: + row=[] + for j in range(len(particle.surface)): + #assumes molecule and particle have same atomtype if not true than atomdistance will give bad value + if atomdistance(self.atoms[i],particle.surface[j],self.atomtype)<=cutoffdistance: + if particle.getbool(j): row.append([i,j]) #if not bonded than can form a possible bond + if row!=[]:possiblebonds.append(row) #need to correct this.... + + #initiate section which assigns bonds into bonding information + bonds=0 + self.bondinginformation=[] #initiate new class member + + #Checks to see if no bonds are possible [2 possible cases] + if possiblebonds==[]: + print 'no possible bonds can be formed' + return + if bondnumber==0: + print 'bondnumber is 0; so, no possible bonds can form' + return + + #section which assigns a bond to the first molecule atom which can be bonded. + self.bondinginformation.append(self.particlebondinginfo(particle,possiblebonds,0)) + bonds+=1 + if bonds==bondnumber: return + del possiblebonds[0] #deletes possible bonds to the first molecule which can be bonded + l=len(self.bondinginformation)-1 + possiblebonds=self.updatepossiblebonds(possiblebonds,self.bondinginformation[l][1]) #updates possiblebonds + #by removing any bonds which contain the newly bonded particle. + + if possiblebonds==[]:return + #section which finds the last molecule atom which can be bonded + l=len(possiblebonds)-1 + while possiblebonds[l]==[]:# to find the last molecule atom which can be bonded + if possiblebonds==[]:return + del possiblebonds[l] #since there are no more possible bonds in this location delete + l-=1 #go to next possible spot where the last molecule atom could be bonded + + #section which assigns a bond to the last molecule atom which can be bonded. + self.bondinginformation.append(self.particlebondinginfo(particle,possiblebonds,l)) + bonds+=1 + if bonds==bondnumber: return + del possiblebonds[l] #deletes possible bonds to the last molecule which can be bonded + l=len(self.bondinginformation)-1 + possiblebonds=self.updatepossiblebonds(possiblebonds,self.bondinginformation[l][1]) + + if bondnumber-bonds>=len(possiblebonds): #the rest of the bonds are assigned in order + while possiblebonds!=[]: + if possiblebonds[0]==[]: #if row of possible bonds is empty then delete the row + del possiblebonds[0] + else: #else find a bond in the row of possible bonds + self.bondinginformation.append(self.particlebondinginfo(particle,possiblebonds,0)) + #dont need to update bonds since possiblebonds will become empty at the same time or before bonds + #is equal to bondnumber + del possiblebonds[0] + l=len(self.bondinginformation)-1 + possiblebonds=self.updatepossiblebonds(possiblebonds,self.bondinginformation[l][1]) + return + else: #the rest of the bonds are assigned randomly + from random import randint #use to randomly choose an index to bond. + while bonds<bondnumber: + if possiblebonds==[]:break #exits while loop when possiblebonds has become an empty set. + #this ensures that in the case there are not egnough viable bonds from possiblebonds to + #reach the bondnumber. Than, the while loop will not become infinite. + l=len(possiblebonds)-1 + i=randint(0,l) + if possiblebonds[i]==[]: #if row of possible bonds is empty then delete the row + del possiblebonds[i] + else: #else find a bond in the row of possible bonds + self.bondinginformation.append(self.particlebondinginfo(particle,possiblebonds,i)) + bonds+=1 + del possiblebonds[i] + l=len(self.bondinginformation)-1 + possiblebonds=self.updatepossiblebonds(possiblebonds,self.bondinginformation[l][1]) + return + + def particlebondinginfo(self,particle,possiblebonds,index1): + """Takes list of possiblebonds and assigns a bond from possiblebonds[index1]. + Then assigns false to particle.setbool at the bonding location + to ensure no more bonds can form with that particle. + Returns the assigned bond.""" + from random import randint #use to randomly choose 0 and 1 where 1 is keep. + for i in range(len(possiblebonds[index1])): + if i==len(possiblebonds[index1])-1: #automattically bonds under this condition + break + else: #bond has random chance of forming + if randint(0,1)==1: #bond forms + break + else: #no bond forms + continue + particle.setbool(possiblebonds[index1][i][1],False)#makes sure no more bonds can form + return possiblebonds[index1][i] + + def updatepossiblebonds(self,possiblebonds,particlenum): + """finds the particle number in the remaining possible bonds than deletes that bonding information. + This algorithm assumes that particlenum can exist only once in each row of possiblebonds.""" + for i in range(len(possiblebonds)): + for j in range(len(possiblebonds[i])): + if possiblebonds[i][j][1]==particlenum: + del possiblebonds[i][j] + break #go to next row of possiblebonds + return possiblebonds + + def bondtoparticle(self,particle,atomid,newid,newcharge): + """Bonds molecule to particle. Particle is a particlesurface object. + Moves the molecule's atoms bonded to the particle to the particle's position. + Alters the atomtype of the molecule's atoms bonded to the particle to newid. + Alters the charge of the molecule's atoms bonded to the particle to newcharge + Because bonds have formed between the molecule's atoms and the particle's atoms, + atoms with atomid on the molecule need to be removed otherwise the molecule's atoms will have to many bonds. + self.deleteatoms will take care of deleting the atoms atomid and + atoms down the polymer chain in the direction away from the molecule/particle bond. + Now remove the bonded particle atoms from the particle surface becaused the bonded molecule atoms + have replaced those particle atoms and their bonds with the rest of the particle. + Newid must be a string. Atomid can now be a list or an integer. + The list is a list of atom's id values and the single integer is atom's atomtype""" + #moves the molecule's atoms bonded to the particle to the particle's position + #need to do this step first before the atom id's and row indexes get out of sync + #which will occur after atoms get deleted. + print 'modifying the bonded molecules atom information' + for i in range(len(self.bondinginformation)): + #patom=particle.getsurfatom(self.bondinginformation[i][1]) + #if self.atomtype=='molecular':#3-5->x,y,z[molecular] + # for j in range(3,6): + # self.modifyatom(self.bondinginformation[i][0]+1,j,patom[j])#uses the atomid here + #elif self.atomtype=='full':#4-6->x,y,z[full] + # for j in range(4,7): + # self.modifyatom(self.bondinginformation[i][0]+1,j,patom[j])#uses the atomid here + #alters the atomtype to newid of the molecule's atoms bonded to the particle. + self.modifyatom(self.bondinginformation[i][0]+1,2,newid) #uses the atomid here + if self.atomtype=='full': + # Alters the charge of the molecule's atoms bonded to the particle to newcharge + self.modifyatom(self.bondinginformation[i][0]+1,3,newcharge) + + #This is a seperate loop so atom id's and row indexes for previous steps wont get out of sync + #create atomnumbers to begin deletion process. + atomnumbers=[] + for i in range(len(self.bondinginformation)): + atomnumbers.append(self.bondinginformation[i][0]+1)#using actual atomnumbers rather than the row index + + #Call algorithm to find all atoms bonded to atomnumbers in the direction of atomid. + #Than delete those atoms and all molecule structures which contain those atoms. + self.deleteatoms(atomnumbers,atomid) + + print 'begining deletion process of the surface atoms for which the molecule atoms have replaced' + #Goes through the bondinginformation and superficially removes the surfaceatom + for i in range(len(self.bondinginformation)): + particle.removesurfatom(self.bondinginformation[i][1]) #uses the row number + #Allows the particle extract method used outside this class to remove this atom. + + def createxyz(self,file,data,routine='mass', values=None): + """Two possible routines one to use the masses from data and the other to use the atom type and values supplied by the user. + The mass version is assessed by setting the routine to 'mass' which is the default method. + The other version is assessed by setting the routine to 'atomtype'. + The other version takes values which is a list containing the value the user wants to assign those atomtypes to. + The atomtypes of the values in the list will start at 1 even if no atoms in molecule use 1. + This makes it easier to find the atomtype and assign the value from the list + All atom data is assumed to have image flags in the data.""" + f=open(file,'w') + f.write('{0}\n'.format(len(self.atoms))) + f.write('atoms\n') + if routine=='mass': + for line in self.atoms: + type=int(line[2])-1 + mass=data.masses[type][1] + if mass=='12.0107': elementnum=6 + elif mass=='15.9994': elementnum=8 + elif mass=='26.9815':elementnum=13 + else: + print 'no matching mass value. The method will exit' + return + l=len(line)-1 + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-5],line[l-4],line[l-3])) + f.close() + elif routine=='atomtype': + for line in self.atoms: + type=int(line[2])-1 + elementnum=values[type] + l=len(line)-1 + f.write('{0} {1} {2} {3}\n'.format(elementnum,line[l-5],line[l-4],line[l-3])) + f.close()