def plot_dos(cl1, cl2 = None, dostype = None, iatom = 0, orbitals = ('s'), up = None, neighbors = 6, show = 1, path = 'dos', xlim = (None, None), ylim = (None,None) ): """ cl1 (CalculationVasp) - object created by add_loop() dostype (str) - control which dos to plot: 'total' - plot total dos 'diff_total' - difference of total dos, use cl2 for second calculation 'partial' - partial dos orbitals (list of str) - any from 's, p, d, py, pz, px, dxy, dyz, dz2, dxz, dx2' where 'p' and 'd' are sums of projections up - 'up2' allows to download the file once again iatom (int) - number of atom starting from 1 to plot DOS by default it is assumed that the last atom is used iatom ([float]*3) - cartesian coordinates of point around which atoms will be found show (bool) - whether to show the dos path (str) - path to folder with images neighbors - number of neighbours around iatom to plot dos on them xlim, ylim (tuple)- limits for plot #0 s 1 py 2 pz 3 px 4 dxy 5 dyz 6 dz2 7 dxz 8 dx2 #In all cases, the units of the l- and site projected DOS are states/atom/energy. """ iatom -= 1 """1. Read dos""" printlog("------Start plot_dos()-----", imp = 'Y') dos = [] for cl in cl1, cl2: if cl == None: continue if not hasattr(cl, "efermi"): cl.read_results('o') printlog(cl.name, 'e_fermi', cl.efermi, imp = 'Y') DOSCAR = cl.get_file('DOSCAR', update = up); printlog('DOSCAR file is ', DOSCAR) dos.append( VaspDos(DOSCAR, cl.efermi) ) #determine number of zero energy i_efermi = int(len(dos[0].energy) * -dos[0].energy[0] / (dos[0].energy[-1] - dos[0].energy[0])) # number of point with zero fermi energy if cl2: i_efermi_e = int(len(dos[1].energy) * -dos[1].energy[0] / (dos[1].energy[-1] - dos[1].energy[0])) # number of point with zero fermi energy """2. Plot dos for different cases""" if dostype == 'total': fit_and_plot(show = show, image_name = os.path.join(path, cl1.name+'.dosTotal'), xlabel = "Energy (eV)", ylabel = "DOS (states/eV)", xlim = xlim, ylim = ylim, Total = (dos[0].energy, smoother(dos[0].dos, 10), 'b-')) elif dostype == 'diff_total': if len(dos) > 1: #calculate dos diff dosd = [(d0 - d1)*e for d0, d1, e in zip(dos[0].dos, dos[1].dos, dos[0].energy)] #calculate difference area = trapz(dosd[:i_efermi], dx=1) printlog("area under dos difference = ", -area, imp = 'Y') fit_and_plot(show = show,image_name = cl1.name+'--'+cl2.name+'.dosTotal_Diff', xlabel = "Energy (eV)", ylabel = "DOS (states/eV)", xlim = xlim, ylim = ylim, Diff_Total = (dos[0].energy, smoother(dosd, 15), 'b-')) else: printlog('You provided only one calculation; could not use diff_total') elif 'partial' in dostype: #Partial dos #1 p carbon, d Ti #0 s 1 py 2 pz 3 px 4 dxy 5 dyz 6 dz2 7 dxz 8 dx2 try: dos[0].site_dos(0, 4) except: printlog('Error! No information about partial dxy dos in DOSCAR; use LORBIT 12 to calculate it') """Determine neighbouring atoms """ printlog("Number of considered neighbors is ", neighbors) if type(iatom) == int: #for the cases when we need to build surrounding around specific atom in this calculation - just use number of atom t = cl1.end.typat[iatom] z = cl1.end.znucl[t-1] el = element_name_inv(z) printlog('Typat of chosen imp atom in cl1 is ', el) surround_center = cl1.end.xcart[iatom] else: #for the case when coordinates of arbitary point are provided. surround_center = iatom el = 'undef' local_atoms = local_surrounding(surround_center, cl1.end, neighbors, control = 'atoms', periodic = True) numbers = local_atoms[2] # first atom is impurity if exist printlog("Numbers of local atoms:", [n+1 for n in numbers] ) printlog("List of distances", [round(d,2) for d in local_atoms[3]] ) iX = numbers[0] for j in range(len(dos)): dos[j].p = [] #central and and surrounding dos[j].d = [] #central atom and surrounding atoms dos[j].d6 = 0 #sum by six atoms for i in numbers: #Now for surrounding atoms in numbers list: plist = [dos[j].site_dos(i, l) for l in (1,2,3) ] dos[j].p.append( [ sum(x) for x in zip(*plist) ] ) dlist = [dos[j].site_dos(i, l) for l in (4,5,6,7,8) ] # For total d T96C dsum = [ sum(x) for x in zip(*dlist) ] dos[j].d.append( dsum ) dos[j].p6 = [ sum(pi) for pi in zip(*dos[j].p) ] #sum over neighbouring atoms dos[j].d6 = [ sum(di) for di in zip(*dos[j].d) ] #sum over neighbouring atoms # t2g = [dos[0].site_dos(iTi, l) for l in 4,5,7] # Now only for first Ti atom # dos[0].t2g = [ sum(x) for x in zip(*t2g) ] # eg = [dos[0].site_dos(iTi, l) for l in 8, 6] # Now only for first Ti atom # dos[0].eg = [ sum(x) for x in zip(*eg) ] """Plotting""" nsmooth = 15 # smooth of dos d1 = dos[0] energy1 = dos[0].energy args = {} i_orb = {'s':0, 'py':1, 'pz':2, 'px':3, 'dxy':4, 'dyz':5, 'dz2':6, 'dxz':7, 'dx2':8} color = {'s':'k-', 'p':'g-', 'd':'b-', 'py':'r-', 'pz':'b-', 'px':'c-', 'dxy':'m-', 'dyz':'c-', 'dz2':'m-', 'dxz':'r-', 'dx2':'g-'} for orb in orbitals: if orb == 'p': args[orb] = (d1.energy, smoother(d1.p[0], nsmooth), color[orb]) elif orb == 'd': args[orb] = (d1.energy, smoother(d1.d[0], nsmooth), color[orb]) else: args[orb] = (d1.energy, smoother(d1.site_dos(iX, i_orb[orb]), nsmooth), color[orb]) image_name = os.path.join(path, cl1.name+'.'+''.join(orbitals)+'.'+el+str(iX)) fit_and_plot(show = show, image_name = image_name, figsize = (4,6), xlabel = "Energy (eV)", ylabel = "DOS (states/eV)", # title = cl1.name.split('.')[0]+'; V='+str(round(cl1.vol) )+' $\AA^3$; Impurity: '+el, xlim = xlim, ylim = ylim, legend = 2, **args ) # printlog("Writing file", image_name, imp = 'Y') """Additional dos analysis; to be refined""" if 0: """Calculate d DOS at Fermi level""" nn = 50 #number to integrate in both directions x1 = dos[0].energy[i_efermi-nn:i_efermi+nn] y1 = smoother(dos[0].d6, nsmooth)[i_efermi-nn:i_efermi+nn] x2 = dos[1].energy[i_efermi_e-nn:i_efermi_e+nn] y2 = smoother(dos[1].d6, nsmooth)[i_efermi_e-nn:i_efermi_e+nn] f1 = interp1d(x1, y1, kind='cubic') f2 = interp1d(x2, y2, kind='cubic') # if debug: print '\n' # if debug: print dos[0].d6[i_efermi] - dos[1].d6[i_efermi_e], " - by points; change of d Ti DOS at the Fermi level due to the carbon" # if debug: print f2(0), f1(0) e_at_Ef_shift = f1(0) - f2(0) printlog("{:5.2f} reduction of d dos at Fermi level; smoothed and interpolated".format( e_at_Ef_shift ), imp = 'Y' ) if 0: """Calculate second derivative of d at the Fermi level""" # tck1 = interpolate.splrep(x1, y1, s=0) # tck2 = interpolate.splrep(x2, y2, s=0) # e_at_Ef_shift_spline = interpolate.splev(0, tck1, der=0) - interpolate.splev(0, tck2, der=0) # if debug: print "{:5.2f} smoothed and interpolated from spline".format( e_at_Ef_shift_spline ) # # if debug: print type(interpolate.splev(0, tck1, der=2)) # if debug: print "{:5.2f} {:5.2f} gb and bulk second derivative at Fermi from spline".format( float(interpolate.splev(0, tck1, der=2)), float(interpolate.splev(0, tck2, der=2)) ) if 0: """Calculate shift of d orbitals after adding impurity""" d_shift = det_gravity(dos[0], Erange = (-2.8, 0)) - det_gravity(dos[1], Erange = (-2.8, 0) ) #negative means impurity shifts states to negative energies, which is favourable printlog( "{:5.2f} Shift of Ti d center of gravity".format( d_shift ), imp = 'Y' ) # if debug: print det_gravity(dos[0], Erange = (-2.8, 0)), det_gravity(dos[1], Erange = (-2.8, 0)) """Calculate correlation between imp p and matrix d""" def rmsdiff(a, b): # rms difference of vectors a and b: #Root-mean-square deviation rmsdiff = 0 for (x, y) in zip(a, b): rmsdiff += (x - y) ** 2 # NOTE: overflow danger if the vectors are long! return math.sqrt(rmsdiff / min(len(a), len(b))) pd_drms = 1/rmsdiff(dos[0].p, dos[0].d6) # the higher the number the higher hybridization printlog("{:5.2f} p-d hybridization estimate".format( pd_drms ) , imp = 'Y') # if debug: print "sqeuclidean", sqeuclidean(dos[0].p, dos[0].d6)/len(dos[0].d6) # if debug: print "pearsonr", pearsonr(dos[0].p, dos[0].d6) #Pearson correlation coefficient; only shape; the larger number means more similarity in shape # def autocorr(x): # result = np.correlate(x, x, mode='full') # return result[result.size/2:] printlog("------End plot_dos()-----\n\n") return {'name':cl1.name}
def pairs(in_calc, xcart_pores, central_atoms, prec=2, max_dist=20, max_dist_from_gb=4, pairtyp="gvol"): """ Searhing for pairs and make list of distances and numbers of atoms prec - precision, allows to control which distances can be related to the same configurations max_dist - maximum distance between atoms in pair max_dist_from_gb - pairtyp - 'gvol' assumes that central_atoms are in the grain volume, 'gb' assumes that central_atoms are in the grain boundary region """ st = in_calc.init st_replic = replic(st, (2, 2, 2)) st_replic = replic(st_replic, (2, 2, 2), -1) # replic in negative direction also r1x = in_calc.rprimd[0][0] r3z = in_calc.rprimd[2][2] print "Half length of r1x is", r1x / 2 if segtyp in ["segreg", "coseg", "grainvol"]: gbpos2 = in_calc.gbpos gbpos1 = gbpos2 - r1x / 2.0 print "\n\nPositions of boundaries gb1 and gb2", gbpos1, gbpos2 print "Maximum possible distance between boundary and impurity", r1x / 4 else: gbpos2 = 0 gbpos1 = 0 dlist = [] d1list = [] d2list = [] dgb2list = [] n_neighbours = 8 # number of atoms to calculate sums sumrulist = [] # list of sums (sumr1 or sumr2) of unique pores unique_pores = [] # the same list but also with coordinates of pores sumrlist = [] # list of sumr1+sumr2 k = 1 d2diff = 0 d1diff = 0 # z1 = 6 #charge of added impurity # z2 = 8 diffprec = 0.02 # print xcart_pores for i, x1 in enumerate(xcart_pores): if i not in central_atoms: continue # iz = z1 for j, x2 in enumerate(xcart_pores): if all(x1 == x2): continue d = abs(x2[0] - in_calc.gbpos) if pairtyp == "gb" and d > max_dist_from_gb: continue # second atom is too far from grain boundary d1, d2 = image_distance(x1, x2, st.rprimd, 2) # the minimum distance and the next minimum dist if d1 > max_dist: continue if (d1, d2) != image_distance(x1, x2, st.rprimd, 3): raise RuntimeError # test, searching in father images # d1 = round(d1,prec) # d2 = round(d2,prec) dgb1 = round(x2[0] - gbpos1, prec) dgb2 = round(gbpos2 - x2[0], prec) sumr1 = local_surrounding(x1, st_replic, n_neighbours) # sum of distances to surrounding atoms sumr2 = local_surrounding(x2, st_replic, n_neighbours) sumr = sumr2 + sumr1 if sumr1 not in sumrulist: sumrulist.append(sumr1) unique_pores.append((sumr1, x1)) # determine unique pores if sumr2 not in sumrulist: sumrulist.append(sumr2) unique_pores.append((sumr2, x2)) # determine unique pores # if d1 in d1list: continue if sumr in sumrlist: # new condition based on sumr ind = sumrlist.index(sumr) i_min, smaller = min_diff(d1, d1list, diffprec) if smaller: continue # if 0:#d1list: # i_min, smaller = min_diff(d1, d1list, diffprec)# d1 has the smallest difference with di # #print "exist" # d2diff = abs(d2list[i_min]-d2) # #print abs(d2list[i_min]-d2) # #print central_atoms # if smaller and abs(d2list[i_min]-d2) < diffprec*2 : continue #and abs(dgb2list[i_min]-dgb2) < diffprec # i_min, smaller = min_diff(d2, d2list, diffprec)# d1 has the smallest difference with di # d1diff = abs(d1list[i_min]-d1) # if smaller and abs(d1list[i_min]-d1) < diffprec*2 : continue # print "skiped" # di, smaller = min_diff(d2, d2list, diffprec) # if di != None and smaller: continue # if min_diff(d2, d2list, diffprec): continue # be carefull here. this condition can pass some unique configrations; should make additional check like below # if d2 in d2list and dgb2list[d2list.index(d2)] == dgb2: continue # jz = z2 sumrlist.append(sumr) d1list.append(d1) # d2list.append(d2) # dgb2list.append(dgb2) sym = "" if 0: # mannualy switched off if abs(x1[1] - x2[1]) < diffprec: # Find symmetry if abs(x1[2] - x2[2]) < diffprec: sym = "ms" # if y and z are the same, than mirror symmetry elif abs(x1[2] - x2[2]) - r3z < diffprec: sym = "is" # inverse symmtry elif ( abs(x1[2] + x2[2]) - 0.5 * r3z < diffprec ): # only for t111g; should be extended for general case of existing periods along y or z sym = "is" dlist.append( [round(d1, prec), round(d2, prec), sym, sumr1, sumr2, dgb1, dgb2, x1, x2, sumr1, sumr2] ) # the first sumr1, sumr2 below replaced by their types k += 1 dlist.sort(key=itemgetter(0)) unique_pores.sort(key=itemgetter(0)) sumrulist.sort() print "Number of unique pores is", len(unique_pores) print "Pores have the following sums: ", unique_pores print "Searching for similar pairs but with different distances ..." print "number, d1, d2, name, sumr1, sumr2, dgb1, dgb2; parallel pair with larger distances" bname = element_name_inv(target_znucl[1]) + element_name_inv(target_znucl[2]) for i, el1 in enumerate(dlist): typ1 = sumrulist.index(el1[3]) + 1 # typ of pore of the first atom typ2 = sumrulist.index(el1[4]) + 1 el1[3] = typ1 el1[4] = typ2 if pairtyp == "gb": dlist[i][2] = bname + "i" + str(i + 1) + "." + str(el1[3]) + "-" + str(el1[4]) + dlist[i][2] elif pairtyp == "gvol": dlist[i][2] = bname + ".v" + str(i + 1) + dlist[i][2] print i + 1, el1[:3], el1[-2:], el1[-6], el1[-5], # number, d1, d2, name, sumr1, sumr2, dgb1, dgb2 for ( el2 ) in ( dlist ): # this loop looks for pairs which are parallel to the same direction as el1 but have larger interdistances if el1 == el2: continue mod = el2[0] / el1[0] % 1 if (mod < 0.005 or mod > 0.995) and abs(el1[0] - el2[0]) > dlist[0][ 0 ]: # only multiple distances and if difference is larger than smallest distance # if round(el1[2],prec-1) != round(el2[2],prec-1): continue #In either case the sum the distances should be the same for the same direction if el1[0] == el2[1]: continue print el2[0] / el1[0], # el2, this pair of atoms is analogus to el1 but have larger interdistance print print "Total number of structures is", len(dlist) if 0: print "\n\nSearching for pairs with equal distances by periodic boundary conditions:" for el1 in dlist: if el1[0] == el1[1]: print el1 print "\nSearching for pairs with not equal distances by periodic boundary conditions:" for el1 in dlist: if el1[0] != el1[1]: print el1 print "\nSearching for pairs with d2/d1>2:" for el1 in dlist: if el1[1] / el1[0] > 2: print el1 dlist[0].append(unique_pores) # last element of dlist[0] is sum and coordinates of unique pores return dlist
def pairs(in_calc, xcart_pores, central_atoms, prec=2, max_dist=20, max_dist_from_gb=4, pairtyp='gvol'): """ Searhing for pairs and make list of distances and numbers of atoms prec - precision, allows to control which distances can be related to the same configurations max_dist - maximum distance between atoms in pair max_dist_from_gb - pairtyp - 'gvol' assumes that central_atoms are in the grain volume, 'gb' assumes that central_atoms are in the grain boundary region """ st = in_calc.init st_replic = replic(st, (2, 2, 2)) st_replic = replic(st_replic, (2, 2, 2), -1) #replic in negative direction also r1x = in_calc.rprimd[0][0] r3z = in_calc.rprimd[2][2] print "Half length of r1x is", r1x / 2 if segtyp in ['segreg', 'coseg', 'grainvol']: gbpos2 = in_calc.gbpos gbpos1 = gbpos2 - r1x / 2. print "\n\nPositions of boundaries gb1 and gb2", gbpos1, gbpos2 print "Maximum possible distance between boundary and impurity", r1x / 4 else: gbpos2 = 0 gbpos1 = 0 dlist = [] d1list = [] d2list = [] dgb2list = [] n_neighbours = 8 # number of atoms to calculate sums sumrulist = [] #list of sums (sumr1 or sumr2) of unique pores unique_pores = [] #the same list but also with coordinates of pores sumrlist = [] #list of sumr1+sumr2 k = 1 d2diff = 0 d1diff = 0 #z1 = 6 #charge of added impurity #z2 = 8 diffprec = 0.02 # print xcart_pores for i, x1 in enumerate(xcart_pores): if i not in central_atoms: continue #iz = z1 for j, x2 in enumerate(xcart_pores): if all(x1 == x2): continue d = abs(x2[0] - in_calc.gbpos) if pairtyp == 'gb' and d > max_dist_from_gb: continue #second atom is too far from grain boundary d1, d2 = image_distance( x1, x2, st.rprimd, 2) # the minimum distance and the next minimum dist if d1 > max_dist: continue if (d1, d2) != image_distance(x1, x2, st.rprimd, 3): raise RuntimeError #test, searching in father images #d1 = round(d1,prec) #d2 = round(d2,prec) dgb1 = round(x2[0] - gbpos1, prec) dgb2 = round(gbpos2 - x2[0], prec) sumr1 = local_surrounding( x1, st_replic, n_neighbours) # sum of distances to surrounding atoms sumr2 = local_surrounding(x2, st_replic, n_neighbours) sumr = sumr2 + sumr1 if sumr1 not in sumrulist: sumrulist.append(sumr1) unique_pores.append((sumr1, x1)) #determine unique pores if sumr2 not in sumrulist: sumrulist.append(sumr2) unique_pores.append((sumr2, x2)) #determine unique pores #if d1 in d1list: continue if sumr in sumrlist: # new condition based on sumr ind = sumrlist.index(sumr) i_min, smaller = min_diff(d1, d1list, diffprec) if smaller: continue # if 0:#d1list: # i_min, smaller = min_diff(d1, d1list, diffprec)# d1 has the smallest difference with di # #print "exist" # d2diff = abs(d2list[i_min]-d2) # #print abs(d2list[i_min]-d2) # #print central_atoms # if smaller and abs(d2list[i_min]-d2) < diffprec*2 : continue #and abs(dgb2list[i_min]-dgb2) < diffprec # i_min, smaller = min_diff(d2, d2list, diffprec)# d1 has the smallest difference with di # d1diff = abs(d1list[i_min]-d1) # if smaller and abs(d1list[i_min]-d1) < diffprec*2 : continue #print "skiped" #di, smaller = min_diff(d2, d2list, diffprec) #if di != None and smaller: continue #if min_diff(d2, d2list, diffprec): continue # be carefull here. this condition can pass some unique configrations; should make additional check like below #if d2 in d2list and dgb2list[d2list.index(d2)] == dgb2: continue #jz = z2 sumrlist.append(sumr) d1list.append(d1) # d2list.append(d2) # dgb2list.append(dgb2) sym = '' if 0: #mannualy switched off if abs(x1[1] - x2[1]) < diffprec: #Find symmetry if abs(x1[2] - x2[2]) < diffprec: sym = 'ms' # if y and z are the same, than mirror symmetry elif abs(x1[2] - x2[2]) - r3z < diffprec: sym = 'is' # inverse symmtry elif abs( x1[2] + x2[2] ) - 0.5 * r3z < diffprec: # only for t111g; should be extended for general case of existing periods along y or z sym = 'is' dlist.append([ round(d1, prec), round(d2, prec), sym, sumr1, sumr2, dgb1, dgb2, x1, x2, sumr1, sumr2 ]) #the first sumr1, sumr2 below replaced by their types k += 1 dlist.sort(key=itemgetter(0)) unique_pores.sort(key=itemgetter(0)) sumrulist.sort() print 'Number of unique pores is', len(unique_pores) print 'Pores have the following sums: ', unique_pores print "Searching for similar pairs but with different distances ..." print "number, d1, d2, name, sumr1, sumr2, dgb1, dgb2; parallel pair with larger distances" bname = element_name_inv(target_znucl[1]) + element_name_inv( target_znucl[2]) for i, el1 in enumerate(dlist): typ1 = sumrulist.index(el1[3]) + 1 #typ of pore of the first atom typ2 = sumrulist.index(el1[4]) + 1 el1[3] = typ1 el1[4] = typ2 if pairtyp == 'gb': dlist[i][2] = bname + 'i' + str(i + 1) + '.' + str( el1[3]) + '-' + str(el1[4]) + dlist[i][2] elif pairtyp == 'gvol': dlist[i][2] = bname + '.v' + str(i + 1) + dlist[i][2] print i + 1, el1[:3], el1[-2:], el1[-6], el1[ -5], #number, d1, d2, name, sumr1, sumr2, dgb1, dgb2 for el2 in dlist: #this loop looks for pairs which are parallel to the same direction as el1 but have larger interdistances if el1 == el2: continue mod = el2[0] / el1[0] % 1 if (mod < 0.005 or mod > 0.995 ) and abs(el1[0] - el2[0]) > dlist[0][ 0]: #only multiple distances and if difference is larger than smallest distance #if round(el1[2],prec-1) != round(el2[2],prec-1): continue #In either case the sum the distances should be the same for the same direction if el1[0] == el2[1]: continue print el2[0] / el1[ 0], # el2, this pair of atoms is analogus to el1 but have larger interdistance print print 'Total number of structures is', len(dlist) if 0: print "\n\nSearching for pairs with equal distances by periodic boundary conditions:" for el1 in dlist: if el1[0] == el1[1]: print el1 print "\nSearching for pairs with not equal distances by periodic boundary conditions:" for el1 in dlist: if el1[0] != el1[1]: print el1 print "\nSearching for pairs with d2/d1>2:" for el1 in dlist: if el1[1] / el1[0] > 2: print el1 dlist[0].append( unique_pores ) # last element of dlist[0] is sum and coordinates of unique pores return dlist