def around_alkali(st, nn, alkali_ion_number): #return numbers and distances to n_neighbours = nn alkali_ions = [] dist = [] ifmaglist = st.get_maglist() for i, typ, x in zip(range(st.natom), st.typat, st.xcart): z = st.znucl[typ-1] if z in header.ALKALI_ION_ELEMENTS: alkali_ions.append([i, z, x]) if len(alkali_ions) > 0: if alkali_ion_number: kk = alkali_ion_number-1 chosen_ion = (kk, st.znucl[st.typat[kk]-1], st.xcart[kk]) else: chosen_ion = alkali_ions[0] #just the first one is used # alkali_ions[min(alkali_ions)] sur = local_surrounding(chosen_ion[2], st, n_neighbours = n_neighbours, control = 'atoms', periodic = True, only_elements = header.TRANSITION_ELEMENTS) # print (sur) dist = np.array(sur[3]).round(2) numb = np.array(sur[2]) else: numb = ifmaglist # if no alk ions show for all mag atoms chosen_ion = None return numb, dist, chosen_ion
def determine_unique_final(st_pores, sums, avds, x_m): final_table = [] insert_positions = [] """Please determine unique positions with similar distances taking into acc PBC!!! below is incorrect """ # crude_prec = 1 # sums_crude = np.unique(sums.round(crude_prec)) # print_and_log('The unique voids based on the sums:', # '\nwith 0.01 A prec:',np.unique(sums.round(2)), # '\nwith 0.1 A prec:',sums_crude, # imp ='y') # print_and_log('Based on crude criteria only', len(sums_crude),'types of void are relevant', imp = 'y') # xcart_unique = [] # avds_unique = [] # sums_unique = [] # for i, s in enumerate(sums_crude): # index_of_first = np.where(sums.round(crude_prec)==s)[0][0] # xcart_unique.append(st_pores.xcart[index_of_first]) # avds_unique.append(avds[index_of_first] ) # sums_unique.append(sums[index_of_first] ) # st_pores_unique = copy.deepcopy(st_pores) # st_pores_unique.xcart = xcart_unique # st_pores_unique.xcart2xred() sur = local_surrounding(x_m, st_pores, n_neighbours=len(st_pores.xcart), control='atoms', periodic=True) # print('neb.determine_unique_final(): sur', sur) print_and_log('I can suggest you ' + str(len(sur[0])) + ' end positions.', imp='y') for i, (x, d, ind) in enumerate(zip(sur[0], sur[3], sur[2])): # if i == 0: # continue final_table.append( [i, np.array(x).round(2), round(d, 2), avds[ind], sums[ind]]) print_and_log(tabulate(final_table, headers=['void #', 'Cart.', 'Dist', 'Dev.', 'Sum'], tablefmt='psql'), imp='Y') return sur
def plot_dos( cl1, cl2=None, dostype=None, iatom=None, iatom2=None, orbitals=('s'), up=None, neighbors=6, show=1, labels=None, path='dos', xlim=(None, None), ylim=(None, None), savefile=True, plot_param={}, suf2='', fontsize=8, nsmooth=12, lts2='--', split_type='octa', plot_spin_pol=1, show_gravity=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 also to sum around neigbours use p6 and d6 and neighbors parameter up - 'up2' allows to download the file once again labels - two manual labels for cl1 and cl2 instead of auto iatom (int) - number of atom starting from 1 to plot DOS; 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 using p6 or d6; only p6 is implemented to the moment in plot section xlim, ylim (tuple)- limits for plot plot_param - dict of parameters to fit_and_plot dashes - control of dahsed lines suf2 - additional suffix # nsmooth = 15 # smooth of dos lts2 - style of lines for cl2 split_type - octa - the names are t2g and eg tetra - the names are t2 and e plot_spin_pol - 0 - spin-polarized components are summed up show_gravity (list) - print gravity centers (i, type, range, ); i - 1 or 2 cl type (str) 'p6' - for p orbitals of neighbors 'p' #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. """ if fontsize: header.mpl.rcParams.update({'font.size': fontsize + 4}) header.mpl.rc('legend', fontsize=fontsize) if dostype == 'partial': eld1, eld2 = {}, {} for i, el in enumerate(cl1.end.get_elements()): eld1[i + 1] = el if cl2: for i, el in enumerate(cl2.end.get_elements()): eld2[i + 1] = el if not iatom: printlog( 'Warning! Please choose atom number *iatom* from the following list:\n' ) printlog(eld) sys.exit() else: printlog('cl1: Atom', iatom, 'of type', eld1[iatom], 'is choosen', imp='y') printlog('cl1: Atom numbers:', eld1, imp='y') printlog('cl1:', determine_symmetry_positions(cl1.end, eld1[iatom]), imp='y') # print(cl2) if cl2: if not iatom2: printlog('Error! provide iatom2!') printlog('cl2: Atom', iatom2, 'of type', eld2[iatom2], 'is choosen', imp='y') printlog('cl2:', determine_symmetry_positions(cl2.end, eld2[iatom2]), imp='y') iatom -= 1 if cl2: if not iatom2: printlog('Error!, provide *iatom2*!') iatom2 -= 1 if 'figsize' not in plot_param: plot_param['figsize'] = (4, 6) if 'legend' not in plot_param: plot_param['legend'] = 'best' """1. Read dos""" printlog("------Start plot_dos()-----", imp='Y') dos = [] # main list for cl1 and cl2 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', nametype='asoutcar') 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 if len(dos[0].dos) == 2: spin_pol = True else: spin_pol = False """2. Plot dos for different cases""" if dostype == 'total': # print(dos[0].dos) ylabel = "DOS (states/eV)" if spin_pol: dosplot = { 'Tot up': { 'x': dos[0].energy, 'y': smoother(dos[0].dos[0], 10), 'c': 'b', 'ls': '-' }, 'Tot down': { 'x': dos[0].energy, 'y': -smoother(dos[0].dos[1], 10), 'c': 'r', 'ls': '-' } } else: dosplot = { 'Total': { 'x': dos[0].energy, 'y': smoother(dos[0].dos, 10), 'c': 'b', 'ls': '-' } } # args[nam_down] = {'x':d.energy, 'y':-smoother(d.site_dos(iat, i_orb_down[orb]), nsmooth), 'c':color[orb], 'ls':l, 'label':None} # xlabel = "Energy (eV)", ylabel = "DOS (states/eV)" # print(plot_param) image_name = os.path.join(path, cl1.name + '.dosTotal') fit_and_plot(show=show, image_name=image_name, hor=True, **plot_param, **dosplot) elif dostype == 'diff_total': #no spin-polarized!!!! ylabel = "DOS (states/eV)" 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)", hor=True, **plot_param, 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 ylabel = "PDOS (states/atom/eV)" 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, imp = 'y') 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, imp='y') 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] els = cl1.end.get_elements() el_sur = els[numbers[1]] # element of surrounding type printlog("Numbers of local atoms:", [n + 1 for n in numbers], imp='Y') printlog("Elements of local atoms:", [els[i] for i in numbers], imp='Y') printlog("List of distances", [round(d, 2) for d in local_atoms[3]], imp='Y') iX = numbers[0] # first atom is impurity if exist # printlog numbers_list = [numbers] # numbers_list is list of lists calcs = [cl1] if cl2: numbers_list.append([iatom2]) # for cl2 only one atom is supported calcs.append(cl2) for cl, d, numbers in zip(calcs, dos, numbers_list): d.p = [] #central and surrounding d.d = [] d.p_up = [] d.p_down = [] d.p_down = [] #central and and surrounding d.d_up = [] #central atom and surrounding atoms d.d_down = [] #central atom and surrounding atoms d.t2g_up = [] d.t2g_down = [] d.eg_up = [] d.eg_down = [] d.p_all = [] #sum over all atoms d.d_all = [] #sum over all atoms d.p_all_up = [] #sum over all atoms d.d_all_up = [] #sum over all atoms d.p_all_down = [] #sum over all atoms d.d_all_down = [] #sum over all atoms if 'p_all' in orbitals or 'd_all' in orbitals: #sum over all atoms p = [] p_up = [] p_down = [] dd = [] d_up = [] d_down = [] els = cl.end.get_elements() for i in range(cl.end.natom): # if 'O' not in els[i]: # continue if spin_pol: plist_up = [d.site_dos(i, l) for l in (2, 4, 6)] plist_down = [d.site_dos(i, l) for l in (3, 5, 7)] plist = plist_up + plist_down p_up.append([sum(x) for x in zip(*plist_up)]) p_down.append([sum(x) for x in zip(*plist_down)]) p.append([sum(x) for x in zip(*plist)]) else: plist = [d.site_dos(i, l) for l in (1, 2, 3)] p.append([sum(x) for x in zip(*plist)]) if spin_pol: dlist_up = [ d.site_dos(i, l) for l in (8, 10, 12, 14, 16) ] # dlist_down = [ d.site_dos(i, l) for l in (9, 11, 13, 15, 17) ] # dlist = dlist_up + dlist_down d_up.append([sum(x) for x in zip(*dlist_up)]) d_down.append([sum(x) for x in zip(*dlist_down)]) dd.append([sum(x) for x in zip(*dlist)]) else: dlist = [d.site_dos(i, l) for l in (4, 5, 6, 7, 8)] # dd.append([sum(x) for x in zip(*dlist)]) d.p_all = [sum(pi) for pi in zip(*p)] #sum over all atoms d.d_all = [sum(di) for di in zip(*dd)] if spin_pol: d.p_all_up = [sum(pi) for pi in zip(*p_up)] d.p_all_down = [sum(pi) for pi in zip(*p_down)] d.d_all_up = [sum(pi) for pi in zip(*d_up)] d.d_all_down = [sum(pi) for pi in zip(*d_down)] #sum by surrounding atoms atoms n_sur = len(numbers) for i in numbers: #Now for surrounding atoms in numbers list: if spin_pol: plist_up = [d.site_dos(i, l) for l in (2, 4, 6)] plist_down = [d.site_dos(i, l) for l in (3, 5, 7)] d.p_up.append([sum(x) for x in zip(*plist_up)]) d.p_down.append([sum(x) for x in zip(*plist_down)]) plist = plist_up + plist_down d.p.append([sum(x) for x in zip(*plist)]) else: plist = [d.site_dos(i, l) for l in (1, 2, 3)] d.p.append([sum(x) for x in zip(*plist)]) if spin_pol: dlist_up = [d.site_dos(i, l) for l in (8, 10, 12, 14, 16)] # dlist_down = [ d.site_dos(i, l) for l in (9, 11, 13, 15, 17) ] # dlist = dlist_up + dlist_down d.d.append([sum(x) for x in zip(*dlist)]) d.d_up.append([sum(x) for x in zip(*dlist_up)]) d.d_down.append([sum(x) for x in zip(*dlist_down)]) t2g_down = [d.site_dos(i, l) for l in (9, 11, 15)] eg_down = [d.site_dos(i, l) for l in (13, 17)] t2g_up = [d.site_dos(i, l) for l in (8, 10, 14)] eg_up = [d.site_dos(i, l) for l in (12, 16)] d.t2g_down.append([sum(x) for x in zip(*t2g_down)]) d.eg_down.append([sum(x) for x in zip(*eg_down)]) d.t2g_up.append([sum(x) for x in zip(*t2g_up)]) d.eg_up.append([sum(x) for x in zip(*eg_up)]) else: dlist = [d.site_dos(i, l) for l in (4, 5, 6, 7, 8)] # d.d.append([sum(x) for x in zip(*dlist)]) d.p6 = [sum(pi) / n_sur for pi in zip(*d.p) ] #sum over neighbouring atoms now only for spin up if spin_pol: d.p6_up = [sum(pi) / n_sur for pi in zip(*d.p_up) ] #sum over neighbouring atoms now only for spin up d.p6_down = [ sum(pi) / n_sur for pi in zip(*d.p_down) ] #sum over neighbouring atoms now only for spin up d.d6 = [sum(di) for di in zip(*d.d)] #sum over neighbouring atoms """Plotting""" # nsmooth = 15 # smooth of dos d1 = dos[0] ds = [d1] names = [] names = [cl1.id[0] + '_at_' + eld1[iatom + 1] + str(iatom + 1)] atoms = [iatom] els = [eld1[iatom + 1]] lts = [ '-', ] #linetypes if cl2: ds.append(dos[1]) d2 = dos[1] # if labels: # names.append(labels[1]) # else: names.append(cl2.id[0] + '_at_' + eld2[iatom2 + 1] + str(iatom2 + 1)) lts.append(lts2) atoms.append(iatom2) els.append(eld2[iatom2 + 1]) if not spin_pol: plot_spin_pol = 0 # could not plot spin polarization for non-spin polarization plot if 'dashes' in plot_param: dashes = plot_param['dashes'] del plot_param['dashes'] else: dashes = (5, 1) energy1 = dos[0].energy args = {} if spin_pol: i_orb = { 's': 0, 'py': 2, 'pz': 4, 'px': 6, 'dxy': 8, 'dyz': 10, 'dz2': 12, 'dxz': 14, 'dx2': 16 } i_orb_down = { 's': 1, 'py': 3, 'pz': 5, 'px': 7, 'dxy': 9, 'dyz': 11, 'dz2': 13, 'dxz': 15, 'dx2': 17 } else: i_orb = { 's': 0, 'py': 1, 'pz': 2, 'px': 3, 'dxy': 4, 'dyz': 5, 'dz2': 6, 'dxz': 7, 'dx2': 8 } # color = {'s':'k', 'p':'#F14343', 'd':'#289191', 'py':'g', 'pz':'b', 'px':'c', 'dxy':'m', 'dyz':'c', 'dz2':'k', 'dxz':'r', 'dx2':'g', 't2g':'b', 'eg':'g', 'p6':'k'} color = { 's': 'k', 'p': '#FF0018', 'd': '#138BFF', 'py': 'g', 'pz': 'b', 'px': 'c', 'dxy': 'm', 'dyz': 'c', 'dz2': 'k', 'dxz': 'r', 'dx2': 'g', 't2g': '#138BFF', 'eg': '#8E12FF', 'p6': '#FF0018', 'p_all': 'r', 'd_all': 'b' } #http://paletton.com/#uid=54-100kwi++bu++hX++++rd++kX # color = {'s':'k', 'p':'r', 'd':'g', 'py':'g', 'pz':'b', 'px':'c', 'dxy':'m', 'dyz':'c', 'dz2':'m', 'dxz':'r', 'dx2':'g'} for orb in orbitals: i = 0 for n, l, iat, el, d in zip(names, lts, atoms, els, ds): if el in ['Fe', 'Co', 'V', 'Mn'] and orb in ['p', 's']: continue if el == 'O' and orb in ('d', 't2g', 'eg', 'dxy', 'dyz', 'dxz', 'dz2', 'dx2'): continue nam = orb nam_down = nam + '_down' # print('name', n) # print('lts', l) if labels: formula = labels[i] else: formula = latex_chem(n.split('.')[0]) i += 1 if spin_pol: nam += '' suf = '; ' + n nam += suf nam_down += suf if orb == 'p': if plot_spin_pol: args[nam] = { 'x': d.energy, 'y': smoother(d.p_up[0], nsmooth), 'c': color[orb], 'ls': l, 'label': formula + ' ' + el + suf2 + ' ' + orb, 'dashes': dashes } args[nam_down] = { 'x': d.energy, 'y': -smoother(d.p_down[0], nsmooth), 'c': color[orb], 'ls': l, 'label': None, 'dashes': dashes } color[orb] = 'c' else: args[nam] = { 'x': d.energy, 'y': smoother(d.p[0], nsmooth), 'c': color[orb], 'ls': l, 'label': formula + ' ' + el + suf2 + ' ' + orb, 'dashes': dashes } elif orb == 'p6': # now spin-polarized components could not be shown if plot_spin_pol: args[nam] = { 'x': d.energy, 'y': smoother(d.p6_up, nsmooth), 'c': color[orb], 'ls': l, 'label': formula + ' ' + el_sur + suf2 + ' p', 'dashes': dashes } args[nam_down] = { 'x': d.energy, 'y': -smoother(d.p6_down, nsmooth), 'c': color[orb], 'ls': l, 'label': None, 'dashes': dashes } else: args[nam] = { 'x': d.energy, 'y': smoother(d.p6, nsmooth), 'c': color[orb], 'ls': l, 'label': formula + ' ' + el_sur + suf2 + ' p', 'dashes': dashes } elif orb == 'd': if plot_spin_pol: args[nam] = { 'x': d.energy, 'y': smoother(d.d_up[0], nsmooth), 'c': color[orb], 'ls': l, 'label': formula + ' ' + el + suf2 + ' ' + orb } args[nam_down] = { 'x': d.energy, 'y': -smoother(d.d_down[0], nsmooth), 'c': color[orb], 'ls': l, 'label': None } color[orb] = 'm' else: args[nam] = { 'x': d.energy, 'y': smoother(d.d[0], nsmooth), 'c': color[orb], 'ls': l, 'label': formula + ' ' + el + suf2 + ' ' + orb } elif orb == 't2g': if split_type == 'octa': orb_name = orb elif split_type == 'tetra': orb_name = 't2' args[nam] = { 'x': d.energy, 'y': smoother(d.t2g_up[0], nsmooth), 'c': color[orb], 'ls': l, 'label': formula + ' ' + el + suf2 + ' ' + orb_name } if spin_pol: args[nam_down] = { 'x': d.energy, 'y': -smoother(d.t2g_down[0], nsmooth), 'c': color[orb], 'ls': l, 'label': None } elif orb == 'eg': if split_type == 'octa': orb_name = orb elif split_type == 'tetra': orb_name = 'e' args[nam] = { 'x': d.energy, 'y': smoother(d.eg_up[0], nsmooth), 'c': color[orb], 'ls': l, 'label': formula + ' ' + el + suf2 + ' ' + orb_name } if spin_pol: args[nam_down] = { 'x': d.energy, 'y': -smoother(d.eg_down[0], nsmooth), 'c': color[orb], 'ls': l, 'label': None } elif orb == 'p_all': if plot_spin_pol: args[nam] = { 'x': d.energy, 'y': smoother(d.p_all_up, nsmooth), 'c': color[orb], 'ls': l, 'label': formula + ' ' + el + suf2 + ' ' + orb } args[nam_down] = { 'x': d.energy, 'y': -smoother(d.p_all_down, nsmooth), 'c': color[orb], 'ls': l, 'label': None } # color[orb] = 'm' else: args[nam] = { 'x': d.energy, 'y': smoother(d.p_all, nsmooth), 'c': color[orb], 'ls': l, 'label': formula + ' ' + el + suf2 + ' ' + orb } elif orb == 'd_all': if plot_spin_pol: args[nam] = { 'x': d.energy, 'y': smoother(d.d_all_up, nsmooth), 'c': color[orb], 'ls': l, 'label': formula + ' ' + el + suf2 + ' ' + orb } args[nam_down] = { 'x': d.energy, 'y': -smoother(d.d_all_down, nsmooth), 'c': color[orb], 'ls': l, 'label': None } # color[orb] = 'm' else: args[nam] = { 'x': d.energy, 'y': smoother(d.d_all, nsmooth), 'c': color[orb], 'ls': l, 'label': formula + ' ' + el + suf2 + ' ' + orb } else: # args[nam] = (d.energy, smoother(d.site_dos(iat, i_orb[orb]), nsmooth), color[orb]+l) args[nam] = { 'x': d.energy, 'y': smoother(d.site_dos(iat, i_orb[orb]), nsmooth), 'c': color[orb], 'ls': l, 'label': formula + ' ' + el + suf2 + ' ' + orb } if spin_pol: args[nam_down] = { 'x': d.energy, 'y': -smoother(d.site_dos(iat, i_orb_down[orb]), nsmooth), 'c': color[orb], 'ls': l, 'label': None } # args[nam_down] = (d.energy, -smoother(d.site_dos(iat, i_orb_down[orb]), nsmooth), color[orb]+l) """Additional dos analysis; to be refined""" if show_gravity: if show_gravity[0] == 1: d = d1 elif show_gravity[0] == 2: d = d2 if show_gravity[2]: erange = show_gravity[2] else: erange = (-100, 0) if show_gravity[1] == 'p6': gc = det_gravity2(d.energy, d.p6, erange) printlog( 'Gravity center for cl1 for p6 for {:} is {:5.2f}'.format( erange, gc), imp='Y') # gc = det_gravity2(d.energy, d.d[0], erange) # printlog('Gravity center for cl1 for d for {:} is {:5.2f}'.format(erange, gc), imp = 'Y') elif show_gravity[1] == 'p': gc = det_gravity2(d.energy, d.p[0], erange) # for first atom for cl2 elif show_gravity[1] == 'p_all': gc = det_gravity2(d.energy, d.p_all, erange) # for first atom for cl2 printlog('Gravity center for cl1 for p_all for {:} is {:5.2f}'. format(erange, gc), imp='Y') plot_param['ver_lines'] = [{'x': gc, 'c': 'k', 'ls': '--'}] """Plot everything""" image_name = os.path.join( path, '_'.join(names) + '.' + ''.join(orbitals) + '.' + el + str(iat + 1)) if 'xlabel' not in plot_param: plot_param['xlabel'] = "Energy (eV)" if 'ylabel' not in plot_param: plot_param['ylabel'] = ylabel fit_and_plot( show=show, image_name=image_name, hor=True, # title = cl1.name.split('.')[0]+'; V='+str(round(cl1.vol) )+' $\AA^3$; Impurity: '+el, **plot_param, **args) # printlog("Writing file", image_name, imp = 'Y') 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, 'filename': image_name}
def add_neb( starting_calc=None, st=None, st_end=None, it_new=None, ise_new=None, i_atom_to_move=None, up='up2', search_type='vacancy_creation', images=None, r_impurity=None, corenum=None, calc_method=['neb'], inherit_option=None, mag_config=None, i_void_start=None, i_void_final=None, atom_to_insert=None, atom_to_move=None, rep_moving_atom=None, end_pos_types_z=None, replicate=None, it_new_folder=None, inherit_magmom=False, x_start=None, xr_start=None, x_final=None, xr_final=None, upload_vts=False, run=False, add_loop_dic=None, old_behaviour=None, ): """ Prepare needed files for NEB Provides several regimes controlled by *search_type* flag: - existing_voids - search for voids around atom and use them as a final position - vacancy_creation - search for neighbors of the same type and make a vacancy as a start position - interstitial_insertion - search for two neighboring voids; use them as start and final positions by inserting atom *atom_to_insert* - None - just use st and st2 as initial and final ###INPUT: - starting_calc (Calculation) - Calculation object with structure - st (Structure) - structure, can be used instead of Calculation - it_new (str) - name for calculation - st_end (Structure) - final structure - i_atom_to_move (int) - number of atom for moving starting from 0; - *mag_config* (int ) - choose magnetic configuration - allows to obtain different localizations of electron - *replicate* (tuple 3*int) - replicate cell along rprimd - i_void_start, i_void_final (int) - position numbers of voids (or atoms) from the suggested lists - atom_to_insert (str) - element name of atom to insert - atom_to_move (str) - element name of atom to move - it_new_folder (str) - section folder - inherit_option (str) - passed only to add_loop - inherit_magmom (bool) - if True than magmom from starting_calc is used, else from set - end_pos_types_z (list of int) - list of Z - type of atoms, which could be considered as final positions in vacancy creation mode - calc_method (list) - 'neb' - 'only_neb' - run only footer - x_start, x_final (array) - explicit xcart coordinates of moving atom for starting and final positions, combined with atom_to_insert - xr_start, xr_final (array) - explicit xred - rep_moving_atom (str)- replace moving atom by needed atom - can be useful than completly different atom is needed. - upload_vts (bool) - if True upload Vasp.pm and nebmake.pl to server - run (bool) - run on server ###RETURN: None ###DEPENDS: ###TODO 1. Take care of manually provided i_atom_to_move in case of replicate flag using init_numbers 2. For search_type == None x_m and x_del should be determined for magnetic searching and for saving their coordinates to struct_des; now their just (0,0,0) """ if old_behaviour: naming_conventions209 = False # else: naming_conventions209 = True # set False to reproduce old behavior before 2.09.2017 # print(atom_to_insert) # sys.exit() calc = header.calc struct_des = header.struct_des varset = header.varset if not add_loop_dic: add_loop_dic = {} if not end_pos_types_z: end_pos_types_z = [] if not hasattr(calc_method, '__iter__'): calc_method = [calc_method] if starting_calc and st: printlog( 'Warning! both *starting_calc* and *st* are provided. I use *starting_calc*' ) st = copy.deepcopy(starting_calc.end) elif starting_calc: st = copy.deepcopy(starting_calc.end) printlog('I use *starting_calc*') elif st: '' printlog('I use *st*') else: printlog( 'Error! no input structure. Use either *starting_calc* or *st*') if corenum == None: if images == 3: corenum = 15 elif images == 5: corenum = 15 elif images == 7: corenum = 14 else: printlog('add_neb(): Error! number of images', images, 'is unknown to me; please provide corenum!') # print(atom_to_insert) # sys.exit() if corenum: # header.corenum = corenum '' else: corenum = header.CORENUM if corenum % images > 0: print_and_log( 'Error! Number of cores should be dividable by number of IMAGES', images, corenum) if not ise_new: ise_new = starting_calc.id[1] printlog('I use', ise_new, 'as ise_new', imp='y') name_suffix = '' st_pores = [] name_suffix += 'n' + str(images) """Replicate cell """ if replicate: print_and_log('You have chosen to replicate the structure by', replicate) st = replic(st, mul=replicate) name_suffix += str(replicate[0]) + str(replicate[1]) + str( replicate[2]) printlog('Search type is ', search_type) if search_type == None: if st_end == None: printlog( 'Error! You have provided search_type == None, st_end should be provided!' ) st1 = st st2 = st_end x_m = (0, 0, 0) x_del = (0, 0, 0) else: """1. Choose atom (or insert) for moving """ if is_list_like(xr_start): x_start = xred2xcart([xr_start], st.rprimd)[0] st1, i_m = st.add_atoms([x_start], atom_to_insert, return_ins=1) x_m = x_start # i_m = st1.find_atom_num_by_xcart(x_start) # print(st1.get_elements()[i_m]) # sys.exit() if i_atom_to_move: nn = str(i_atom_to_move + 1) else: nn = str(i_void_start) name_suffix += atom_to_insert + nn write_xyz(st1, file_name=st.name + '_manually_start') printlog('Start position is created manually by adding xr_start', xr_start, x_start) type_atom_to_move = atom_to_insert el_num_suffix = '' else: atoms_to_move = [] atoms_to_move_types = [] # print('d', i_atom_to_move) # sys.exit() if i_atom_to_move: typ = st.get_elements()[i_atom_to_move] printlog('add_neb(): atom', typ, 'will be moved', imp='y') atoms_to_move.append( [i_atom_to_move, typ, st.xcart[i_atom_to_move]]) atoms_to_move_types.append(typ) if naming_conventions209: name_suffix += typ + str(i_atom_to_move + 1) else: #try to find automatically among alkali - special case for batteries for i, typ, x in zip(range(st.natom), st.get_elements(), st.xcart): if typ in ['Li', 'Na', 'K', 'Rb', 'Mg']: atoms_to_move.append([i, typ, x]) if typ not in atoms_to_move_types: atoms_to_move_types.append(typ) if atoms_to_move: # print(atom_to_move) # sys.exit() if not atom_to_move: atom_to_move = atoms_to_move_types[ 0] # taking first found element if len(atoms_to_move_types) > 1: printlog( 'Error! More than one type of atoms available for moving detected', atoms_to_move_types, 'please specify needed atom with *atom_to_move*') type_atom_to_move = atom_to_move #atoms_to_move[0][1] # printlog('atom ', type_atom_to_move, 'will be moved', imp ='y') if i_atom_to_move: printlog('add_neb(): *i_atom_to_move* = ', i_atom_to_move, 'is used', imp='y') numbers = [[i_atom_to_move]] i_void_start = 1 else: printlog('add_neb(): determine_symmetry_positions ...', imp='y') numbers = determine_symmetry_positions(st, atom_to_move) # print(numbers) # sys.exit() if len(numbers) > 0: printlog('Please choose position using *i_void_start* :', [i + 1 for i in range(len(numbers))], imp='y') printlog('*i_void_start* = ', i_void_start) i_m = numbers[i_void_start - 1][0] printlog('Position', i_void_start, 'chosen, atom:', i_m + 1, type_atom_to_move, imp='y') else: i_m = numbers[0][0] x_m = st.xcart[i_m] el_num_suffix = type_atom_to_move + str(i_m + 1) atom_to_insert = atom_to_move st1 = st # elif atom_to_replace: # num = st.get_specific_elements(atom_to_replace) # if len(n)>0: # printlog('Please choose position using *i_void_start* :', [i+1 for i in range(len(num))],imp = 'y' ) # printlog('*i_void_start* = ', i_void_start) # i_m = num[i_void_start-1] # printlog('Position',i_void_start,'chosen, atom to replace:', i_m+1, atom_to_replace, imp = 'y' ) # sys.exit() else: print_and_log( 'No atoms to move found, you probably gave me deintercalated structure', important='y') st_pores, sums, avds = determine_voids(st, r_impurity, step_dec=0.1, fine=2) insert_positions = determine_unique_voids(st_pores, sums, avds) print_and_log( 'Please use *i_void_start* to choose the void for atom insertion from the Table above:', end='\n', imp='Y') if i_void_start == None: sys.exit() if atom_to_insert == None: printlog('Error! atom_to_insert = None') st = st.add_atoms([ insert_positions[i_void_start], ], atom_to_insert) name_suffix += 'i' + str(i_void_start) i_m = st.natom - 1 x_m = st.xcart[i_m] search_type = 'existing_voids' type_atom_to_move = atom_to_insert el_num_suffix = '' st1 = st """2. Choose final position""" if is_list_like(xr_final): x_final = xred2xcart([xr_final], st.rprimd)[0] #old #check if i_atom_to_move should be removed # st2 = st1.del_atom(i_m) # st2 = st2.add_atoms([x_final], atom_to_insert) #new st2 = st1.mov_atoms(i_m, x_final) # st1.printme() # st2.printme() # sys.exit() x_del = x_final search_type = 'manual_insertion' name_suffix += 'v' + str(i_void_final) write_xyz(st2, file_name=st.name + '_manually_final') printlog('Final position is created manually by adding xr_final', xr_final, x_del) elif search_type == 'existing_voids': #Search for voids around choosen atoms if not st_pores: st_pores, sums, avds = determine_voids(st, r_impurity) sur = determine_unique_final(st_pores, sums, avds, x_m) print_and_log('Please choose *i_void_final* from the Table above:', end='\n', imp='Y') if i_void_final == None: sys.exit() x_final = sur[0][i_void_final] # printlog('You chose:', np.array(x_final).round(2), end='\n', imp='Y') x_del = x_final #please compare with vacancy creation mode write_xyz(st.add_atoms([x_final], 'H'), replications=(2, 2, 2), file_name=st.name + '_possible_positions2_replicated') print_and_log('Choosing the closest position as end', important='n') st1 = st st2 = st.mov_atoms(i_m, x_final) name_suffix += el_num_suffix + 'e' + str( i_void_final) + atom_to_insert st1 = return_atoms_to_cell(st1) st2 = return_atoms_to_cell(st2) write_xyz(st1, file_name=st1.name + name_suffix + '_start') write_xyz(st2, file_name=st2.name + name_suffix + '_final') elif search_type == 'vacancy_creation': #Create vacancy by removing some neibouring atom of the same type print_and_log( 'You have chosen vacancy_creation mode of add_neb tool', imp='Y') print_and_log('Type of atom to move = ', type_atom_to_move, imp='y') # print 'List of left atoms = ', np.array(st.leave_only(type_atom_to_move).xcart) sur = local_surrounding(x_m, st, n_neighbours=12, control='atoms', only_elements=[invert(type_atom_to_move)] + end_pos_types_z, periodic=True) #exclude the atom itself # print(x_m) # print(sur) # st.nn() print_and_log( 'I can suggest you ' + str(len(sur[0][1:])) + ' end positions. The distances to them are : ', np.round(sur[3][1:], 2), ' A\n ', 'They are ', type_atom_to_move, [invert(z) for z in end_pos_types_z], 'atoms, use *i_void_final* to choose required: 1, 2, 3 ..', imp='y') if not i_void_final: i_void_final = 1 #since zero is itself print_and_log('Choosing position ', i_void_final, 'with distance', round(sur[3][i_void_final], 2), 'A', imp='y') name_suffix += el_num_suffix + 'v' + str(i_void_final) x_del = sur[0][i_void_final] printlog('xcart of atom to delete', x_del) i_del = st.find_atom_num_by_xcart(x_del) # print(x_del) # print(st.xcart) # for x in st.xcart: # if x[0] > 10: # print(x) print_and_log('number of atom to delete = ', i_del, imp='y') if i_del == None: printlog('add_neb(): Error! I could find atom to delete!') # print st.magmom # print st1.magmom # try: if is_list_like(xr_start): st2 = st1.mov_atoms( i_m, x_del) # i_m and sur[0][neb_config] should coincide # i_del = st1.find_atom_num_by_xcart(x_del) st1 = st1.del_atom(i_del) else: print_and_log( 'Making vacancy at end position for starting configuration', imp='y') st1 = st.del_atom(i_del) print_and_log( 'Making vacancy at start position for final configuration', important='n') st2 = st.mov_atoms( i_m, x_del) # i_m and sur[0][neb_config] should coincide # except: # st2 = st st2 = st2.del_atom(i_del) # these two steps provide the same order """Checking correctness of path""" #if start and final positions are used, collisions with existing atoms are possible if is_list_like(xr_start) and is_list_like(xr_final): printlog('Checking correctness') st1, _, _ = st1.remove_close_lying() stt = st1.add_atoms([ x_final, ], 'Pu') stt, x, _ = stt.remove_close_lying( rm_both=True ) # now the final position is empty for sure; however the order can be spoiled # print(st._removed) if stt._removed: st1 = stt # only if overlapping was found we assign new structure st2, _, _ = st2.remove_close_lying(rm_first=stt._removed) stt = st2.add_atoms([ x_start, ], 'Pu') stt, x, _ = stt.remove_close_lying( rm_both=True) # now the start position is empty for sure if stt._removed: st2 = stt print(st2.get_elements()) # sys.exit() elif is_list_like(xr_final) and not is_list_like(xr_start) or is_list_like( xr_start) and not is_list_like(xr_final): printlog( 'Attention! only start of final position is provided, please check that everything is ok with start and final states!!!' ) """ Determining magnetic moments """ vp = varset[ise_new].vasp_params if search_type != None: #for None not implemented; x_m should be determined first for this if 'ISPIN' in vp and vp['ISPIN'] == 2: print_and_log( 'Magnetic calculation detected. Preparing spin modifications ...', imp='y') cl_test = CalculationVasp(varset[ise_new]) cl_test.init = st1 # print 'asdfsdfasdfsadfsadf', st1.magmom if inherit_magmom and hasattr(st, 'magmom') and st.magmom and any( st.magmom): print_and_log( 'inherit_magmom=True: You have chosen MAGMOM from provided structure', imp='y') name_suffix += 'mp' #Magmom from Previous else: cl_test.init.magmom = None print_and_log( 'inherit_magmom=False or no magmom in input structure : MAGMOM will be determined from set', imp='y') name_suffix += 'ms' #Magmom from Set cl_test.actualize_set() #find magmom for current structure st1.magmom = copy.deepcopy(cl_test.init.magmom) st2.magmom = copy.deepcopy(cl_test.init.magmom) # sys.exit() # print_and_log('The magnetic moments from set:') # print cl_test.init.magmom #checking for closest atoms now only for Fe, Mn, Ni, Co sur = local_surrounding(x_m, st1, n_neighbours=3, control='atoms', periodic=True, only_elements=header.TRANSITION_ELEMENTS) dist = np.array(sur[3]).round(2) numb = np.array(sur[2]) a = zip(numb, dist) # a= np.array(a) # print a[1] # a = np.apply_along_axis(np.unique, 1, a) # print a def unique_by_key(elements, key=None): if key is None: # no key: the whole element must be unique key = lambda e: e return list({key(el): el for el in elements}.values()) # print a mag_atoms_dists = unique_by_key(a, key=itemgetter(1)) # print (mag_atoms_dists) # a = unique_by_key(a, key=itemgetter(1)) print_and_log( 'I change spin for the following atoms:\ni atom dist\n', np.round(mag_atoms_dists, 2), imp='y') # print 'I have found closest Fe atoms' muls = [(1.2, 0.6), (0.6, 1.2)] mag_moments_variants = [] for mm in muls: mags = copy.deepcopy(cl_test.init.magmom) # print mags for a, m in zip(mag_atoms_dists, mm): # print t[1] mags[a[0]] = mags[a[0]] * m mag_moments_variants.append(mags) print_and_log('The list of possible mag_moments:', imp='y') for i, mag in enumerate(mag_moments_variants): print_and_log(i, mag) print_and_log( 'Please use *mag_config* arg to choose desired config', imp='y') if mag_config != None: st1.magmom = copy.deepcopy(mag_moments_variants[mag_config]) st2.magmom = copy.deepcopy(mag_moments_variants[mag_config]) name_suffix += 'm' + str(mag_config) print_and_log('You have chosen mag configuration #', mag_config, imp='y') else: print_and_log('Non-magnetic calculation continue ...') """3. Add to struct_des, create geo files, check set, add_loop """ if starting_calc: it = starting_calc.id[0] it_new = it + 'v' + str(starting_calc.id[2]) + '.' + name_suffix if not it_new_folder: it_new_folder = struct_des[it].sfolder + '/neb/' obtained_from = str(starting_calc.id) if not ise_new: print_and_log('I will run add_loop() using the same set', important='Y') ise_new = cl.id[1] elif st: if not it_new: printlog( 'Error! please provide *it_new* - name for your calculation', important='Y') it = None it_new += '.' + name_suffix obtained_from = st.name if not ise_new: printlog('Error! please provide *ise_new*', important='Y') if not it_new_folder: printlog( 'Error! please provide *it_new_folder* - folder for your new calculation', important='Y') if rep_moving_atom: it_new += 'r' + rep_moving_atom if it_new not in struct_des: add_des(struct_des, it_new, it_new_folder, 'Automatically created and added from ' + obtained_from) print_and_log( 'Creating geo files for starting and final configurations (versions 1 and 2) ', important='y') # if starting_calc: # cl = copy.deepcopy(starting_calc) # else: cl = CalculationVasp() #write start position struct_des[it_new].x_m_ion_start = x_m struct_des[it_new].xr_m_ion_start = xcart2xred([x_m], st1.rprimd)[0] # st1, _, _ = st1.remove_close_lying() # st2, _, _ = st2.remove_close_lying() i1 = st1.find_atom_num_by_xcart(x_m, prec=0.3) i2 = st2.find_atom_num_by_xcart(x_del, prec=0.3) if rep_moving_atom: #replace the moving atom by required st1 = st1.replace_atoms([i1], rep_moving_atom) st2 = st2.replace_atoms([i2], rep_moving_atom) else: #allows to make correct order for nebmake.pl st1 = st1.replace_atoms([i1], type_atom_to_move) st2 = st2.replace_atoms([i2], type_atom_to_move) i1 = st1.find_atom_num_by_xcart(x_m, prec=0.3) # the positions were changed i2 = st2.find_atom_num_by_xcart(x_del, prec=0.3) cl.end = st1 ver_new = 1 cl.version = ver_new cl.path["input_geo"] = header.geo_folder + struct_des[it_new].sfolder + '/' + \ it_new+"/"+it_new+'.auto_created_starting_position_for_neb_'+str(search_type)+'.'+str(ver_new)+'.'+'geo' cl.write_siman_geo(geotype='end', description='Starting conf. for neb from ' + obtained_from, override=True) #write final position struct_des[it_new].x_m_ion_final = x_del struct_des[it_new].xr_m_ion_final = xcart2xred([x_del], st2.rprimd)[0] cl.end = st2 ver_new = 2 cl.version = ver_new cl.path["input_geo"] = header.geo_folder + struct_des[it_new].sfolder + '/' + \ it_new+"/"+it_new+'.auto_created_final_position_for_neb_'+str(search_type)+'.'+str(ver_new)+'.'+'geo' cl.write_siman_geo(geotype='end', description='Final conf. for neb from ' + obtained_from, override=True) if not rep_moving_atom: st1s = st1.replace_atoms([i1], 'Pu') st2s = st2.replace_atoms([i2], 'Pu') else: st1s = copy.deepcopy(st1) st2s = copy.deepcopy(st2) vec = st1.center_on(i1) st1s = st1s.shift_atoms(vec) st2s = st2s.shift_atoms(vec) write_xyz(st1s, file_name=it_new + '_start') write_xyz(st2s, file_name=it_new + '_end') st1s.write_poscar('xyz/POSCAR1') st2s.write_poscar('xyz/POSCAR2') # print(a) # runBash('cd xyz; mkdir '+it_new+'_all;'+"""for i in {00..04}; do cp $i/POSCAR """+ it_new+'_all/POSCAR$i; done; rm -r 00 01 02 03 04') with cd('xyz'): a = runBash(header.PATH2NEBMAKE + ' POSCAR1 POSCAR2 3') dst = it_new + '_all' makedir(dst + '/any') for f in ['00', '01', '02', '03', '04']: shutil.move(f + '/POSCAR', dst + '/POSCAR' + f) shutil.rmtree(f) #prepare calculations # sys.exit() #Check if nebmake is avail # if int(runBash('ssh '+cluster_address+' test -e '+project_path_cluster+'/tools/vts/nebmake.pl; echo $?') ): # '' # print_and_log('Please upload vtsttools to ',cluster_address, project_path_cluster+'/tools/vts/') # raise RuntimeError # copy_to_server(path_to_wrapper+'/vtstscripts/nebmake.pl', to = project_path_cluster+'/tools/', addr = cluster_address) # if int(runBash('ssh '+cluster_address+' test -e '+project_path_cluster+'/tools/Vasp.pm; echo $?') ): # copy_to_server(path_to_wrapper+'/vtstscripts/Vasp.pm', to = project_path_cluster+'/tools/', addr = cluster_address) inherit_ngkpt(it_new, it, varset[ise_new]) add_loop(it_new, ise_new, verlist=[1, 2], up=up, calc_method=calc_method, savefile='oc', inherit_option=inherit_option, n_neb_images=images, corenum=corenum, run=run, **add_loop_dic) if upload_vts: siman_dir = os.path.dirname(__file__) # print(upload_vts) push_to_server([ siman_dir + '/cluster_tools/nebmake.pl', siman_dir + '/cluster_tools/Vasp.pm' ], to=header.cluster_home + '/tools/vts', addr=header.cluster_address) else: print_and_log('Please be sure that vtsttools are at', header.cluster_address, header.cluster_home + '/tools/vts/', imp='Y') printlog('add_neb finished') return it_new
def add_neb(starting_calc = None, st = None, it_new = None, ise_new = None, i_atom_to_move = None, up = 'up1', search_type = 'vacancy_creation', images = 3, r_impurity = None, corenum = 15, calc_method = ['neb'], inherit_option = None, mag_config = None, i_void_start = None, i_void_final = None, atom_to_insert = None, replicate = None, it_new_folder = None, inherit_magmom = False, x_start = None, xr_start = None, x_final = None, xr_final = None, upload_vts = False, run = False ): """ Prepare needed files for NEB Provides several regimes controlled by *search_type* flag: - existing_voids - search for voids around atom and use them as a final position - vacancy_creation - search for neighbors of the same type and make a vacancy as a start position - interstitial_insertion - search for two neighboring voids; use them as start and final positions by inserting atom *atom_to_insert* ###INPUT: - starting_calc (Calculation) - Calculation object with structure - st (Structure) - structure, can be used instead of Calculation - it_new (str) - name for calculation - i_atom_to_move (int) - number of atom for moving; - *mag_config* (int ) - choose magnetic configuration - allows to obtain different localizations of electron - *replicate* (tuple 3*int) - replicate cell along rprimd - i_void_start, i_void_final (int) - number of voids from the suggested lists - atom_to_insert (str) - element name of atom to insert - it_new_folder (str) - section folder - inherit_option (str) - passed only to add_loop - inherit_magmom (bool) - if True than magmom from starting_calc is used, else from set - calc_method (list) - 'neb' - 'only_neb' - run only footer - x_start, x_final (array) - explicit coordinates of moving atom for starting and final positions, combined with atom_to_insert - upload_vts (bool) - if True upload Vasp.pm and nebmake.pl to server - run (bool) - run on server ###RETURN: None ###DEPENDS: ###TODO please take care of manually provided i_atom_to_move in case of replicate flag using init_numbers """ calc = header.calc struct_des = header.struct_des varset = header.varset if not hasattr(calc_method, '__iter__'): calc_method = [calc_method] if starting_calc and st: printlog('Warning! both *starting_calc* and *st* are provided. I use *starting_calc*') st = copy.deepcopy(starting_calc.end) elif starting_calc: st = copy.deepcopy(starting_calc.end) printlog('I use *starting_calc*') elif st: '' printlog('I use *st*') else: printlog('Error! no input structure. Use either *starting_calc* or *st*') if corenum: # header.corenum = corenum '' else: corenum = header.CORENUM if corenum % images > 0: print_and_log('Error! Number of cores should be dividable by number of IMAGES') name_suffix = '' st_pores = [] name_suffix+='n'+str(images) """Replicate cell """ if replicate: print_and_log('You have chosen to replicate the structure by', replicate) st = replic(st, mul = replicate) name_suffix += str(replicate[0])+str(replicate[1])+str(replicate[2]) """1. Choose atom (or insert) for moving """ atoms_to_move = [] for i, typ, x in zip(range(st.natom), st.typat, st.xcart): #try to find automatically if st.znucl[typ-1] == 3: #Li atoms_to_move.append([i, 'Li', x]) if st.znucl[typ-1] == 11: # atoms_to_move.append([i, 'Na', x]) if st.znucl[typ-1] == 19: # atoms_to_move.append([i, 'K', x]) if is_list_like(xr_start): x_start = xred2xcart([xr_start], st.rprimd)[0] st1 = st.add_atoms([x_start], atom_to_insert) x_m = x_start name_suffix+='s' write_xyz(st1, file_name = st.name+'_manually_start') printlog('Start position is created manually by adding xr_start', xr_start, x_start) elif not atoms_to_move: print_and_log('No atoms to move found, you probably gave me intercalated structure', important = 'y') print_and_log('Searching for voids', important = 'y') st_pores = find_pores(st, r_matrix = 0.5, r_impurity = r_impurity, fine = 1, calctype = 'all_pores') print_and_log('List of found voids:\n', np.array(st_pores.xcart) ) write_xyz(st.add_atoms(st_pores.xcart, 'H'), file_name = st.name+'_possible_positions') write_xyz(st.add_atoms(st_pores.xcart, 'H'), replications = (2,2,2), file_name = st.name+'_possible_positions_replicated') sums = [] avds = [] for x in st_pores.xcart: summ = local_surrounding(x, st, n_neighbours = 6, control = 'sum', periodic = True) avd = local_surrounding(x, st, n_neighbours = 6, control = 'av_dev', periodic = True) # print sur, sums.append(summ) avds.append(avd[0]) # print sums = np.array(sums) avds = np.array(avds).round(0) print_and_log('Sum of distances to 6 neighboring atoms for each void (A):\n', sums, imp ='y') print_and_log('Distortion of voids (0 - is symmetrical):\n', avds, imp ='y') crude_prec = 1 sums_crude = np.unique(sums.round(crude_prec)) print_and_log('The unique voids based on the sums:', '\nwith 0.01 A prec:',np.unique(sums.round(2)), '\nwith 0.1 A prec:',sums_crude, imp ='y') print_and_log('Based on crude criteria only', len(sums_crude),'types of void are relevant') print_and_log('Please use *i_void_start* to choose the void for atom insertion from this Table:', end = '\n', imp = 'Y') insert_positions = [] start_table = [] for i, s in enumerate(sums_crude): index_of_first = np.where(sums.round(crude_prec)==s)[0][0] start_table.append([i, st_pores.xcart[index_of_first].round(2), index_of_first, avds[index_of_first], sums[index_of_first] ]) insert_positions.append( st_pores.xcart[index_of_first] ) print_and_log( tabulate(start_table, headers = ['Start void #', 'Cart.', 'Index', 'Dev.', 'Sum'], tablefmt='psql'), imp = 'Y' ) if i_void_start == None: sys.exit() st = st.add_atoms([insert_positions[i_void_start],], atom_to_insert) name_suffix+='i'+str(i_void_start) i_m = st.natom-1 x_m = st.xcart[i_m] search_type = 'existing_voids' type_atom_to_move = atom_to_insert el_num_suffix = '' else: print_and_log('I have found', len(atoms_to_move), ' anion atoms', important = 'n') print_and_log( 'Sums of bond lengths around these atoms:',) sums = [] for a in atoms_to_move: summ = local_surrounding(a[2], st, n_neighbours = 6, control = 'sum', periodic = True) sums.append(summ) # print( summ, end = '') print_and_log('\nAmong them only',len(set(sums)), 'unique' , important = 'n') # if print_and_log('Choosing the first' , important = 'n') type_atom_to_move = atoms_to_move[0][1] i_atom_to_move = atoms_to_move[0][0]+1 el_num_suffix = type_atom_to_move +str(i_atom_to_move) i_m = i_atom_to_move-1 x_m = st.xcart[i_m] #highlight the moving atom for user for double-check # st_new = st.change_atom_z(i_m, new_z = 100) # search_type = 'vacancy_creation' """2. Choose final position""" if is_list_like(xr_final): x_final = xred2xcart([xr_final], st.rprimd)[0] st2 = st.add_atoms([x_final], atom_to_insert) x_del = x_final search_type = 'manual_insertion' name_suffix+='f'+atom_to_insert write_xyz(st2, file_name = st.name+'_manually_final') printlog('Final position is created manually by adding xr_final', xr_final, x_del) elif search_type == 'existing_voids': #Search for voids around choosen atoms if not st_pores: st_pores = find_pores(st, r_matrix = 0.5, r_impurity = r_impurity, fine = 2, calctype = 'all_pores') sur = local_surrounding(x_m, st_pores, n_neighbours = len(st_pores.xcart), control = 'atoms', periodic = True) # print sur print_and_log( 'I can suggest you '+str (len(sur[0])-1 )+' end positions.' ) # The distances to them are : '+str(np.round(sur[3], 2) )+' A\n ', # 'Openning Jmol end positions are highlighted by inserting H ', important = 'y') # print x_m # print sur[0] print_and_log('Please choose *i_void_final* from the following Table:', end = '\n', imp = 'Y') final_table = [] for i, (x, d, ind) in enumerate( zip(sur[0], sur[3], sur[2])[1:] ): final_table.append([i, np.array(x).round(2), round(d, 2), avds[ind], sums[ind] ] ) print_and_log( tabulate(final_table, headers = ['Final void #', 'Cart.', 'Dist', 'Dev.', 'Sum'], tablefmt='psql'), imp = 'Y' ) if i_void_final == None: sys.exit() x_final = sur[0][i_void_final+1] # +1 because first element is x_m atom itself write_xyz(st.add_atoms([ x_final], 'H'), replications = (2,2,2), file_name = st.name+'_possible_positions2_replicated') # sys.exit() # write_xyz(st.add_atoms(sur[0][2:3], 'H'), analysis = 'imp_surrounding', show_around = 230,nnumber = 10, replications = (2,2,2), file_name = 'local230') # # write_xyz(st.add_atoms(sur[0][0:1], 'H'), analysis = 'imp_surrounding', show_around = 226,nnumber = 10, replications = (2,2,2), file_name = 'local') # run_jmol print_and_log('Choosing the closest position as end', important = 'n') # i_void_final = 0 st1 = st # print st1.natom # sys.exit() st2 = st.mov_atoms(i_m, x_final) name_suffix += el_num_suffix+'e'+str(i_void_final)+atom_to_insert st1 = return_atoms_to_cell(st1) st2 = return_atoms_to_cell(st2) write_xyz(st1, file_name = st1.name+name_suffix +'_start') write_xyz(st2, file_name = st2.name+name_suffix +'_final') elif search_type == 'vacancy_creation': #Create vacancy by removing some neibouring atom of the same type print_and_log('You have chosen vacancy_creation mode of add_neb tool', important = 'Y') print_and_log( 'Type of atom to move = ', type_atom_to_move, imp = 'y') # print 'List of left atoms = ', np.array(st.leave_only(type_atom_to_move).xcart) sur = local_surrounding(x_m, st.leave_only(type_atom_to_move) , n_neighbours = 4, control = 'atoms', periodic = False) # print 'xcart of moving atom', x_m # print 'Local surround = ', sur # print 'len', len(sur[0]) if len(sur[0]) < 3: # print 'rprimd = \n',np.array(st.rprimd) # print 'r lengths = \n',( [np.linalg.norm(r) for r in st.rprimd] ) # print 'xred = \n', np.array(st.xred) # print 'xcart = \n', np.array(st.xcart) print_and_log('The supercell is too small, I increase it 8 times!') st = replic(st, mul = (2,2,2) ) sur = local_surrounding(x_m, st.leave_only(type_atom_to_move) , n_neighbours = 4, control = 'atoms', periodic = False) # print 'xcart of moving atom', x_m write_xyz(st, file_name = st.name+'_replicated')#replications = (2,2,2)) # print 'Local surround = ', sur # sys.exit() print_and_log( 'I can suggest you '+str (len(sur[0]) )+' end positions. The distances to them are : '+str(np.round(sur[3], 2) )+' A\n ', 'They are all', type_atom_to_move, 'atoms', important = 'y') print_and_log('Choosing the closest position as end', important = 'n') neb_config = 1 #cause the first item in sur is moving atom itself x_del = sur[0][neb_config] i_del = st.find_atom_num_by_xcart(x_del) print_and_log('Making vacancy at end position for starting configuration', important = 'n') print_and_log( 'number of atom to delete = ', i_del) # print st.magmom st1 = st.del_atom(i_del) # print st1.magmom print_and_log('Making vacancy at start position for final configuration', important = 'n') st2 = st.mov_atoms(i_m, x_del) # i_m and sur[0][neb_config] should coincide st2 = st2.del_atom(i_del) # these two steps provide the same order name_suffix += el_num_suffix+'v'+str(neb_config) write_xyz(st1, file_name = st1.name+'_start')# replications = (2,2,2)) write_xyz(st2, file_name = st2.name+'_end')# replications = (2,2,2)) # sys.exit() # sys.exit() """ Determining magnetic moments """ if varset[ise_new].vasp_params['ISPIN'] == 2: print_and_log('Magnetic calculation detected. Preparing spin modifications ...', imp = 'y') cl_test = CalculationVasp(varset[ise_new]) cl_test.init = st1 # print 'asdfsdfasdfsadfsadf', st1.magmom if inherit_magmom and hasattr(st, 'magmom') and st.magmom and any(st.magmom): print_and_log('inherit_magmom=True: You have chosen MAGMOM from provided structure', imp = 'y') name_suffix+='mp' #Magmom from Previous else: cl_test.init.magmom = None print_and_log('inherit_magmom=False or no magmom in input structure : MAGMOM will be determined from set', imp = 'y') name_suffix+='ms' #Magmom from Set cl_test.actualize_set() #find magmom for current structure st1.magmom = copy.deepcopy(cl_test.init.magmom) st2.magmom = copy.deepcopy(cl_test.init.magmom) # sys.exit() # print_and_log('The magnetic moments from set:') # print cl_test.init.magmom #checking for closest atoms now only for Fe, Mn, Ni, Co sur = local_surrounding(x_m, st1, n_neighbours = 3, control = 'atoms', periodic = True, only_elements = header.TRANSITION_ELEMENTS) dist = np.array(sur[3]).round(2) numb = np.array(sur[2]) a = zip(numb, dist ) # a= np.array(a) # print a[1] # a = np.apply_along_axis(np.unique, 1, a) # print a def unique_by_key(elements, key=None): if key is None: # no key: the whole element must be unique key = lambda e: e return list ( {key(el): el for el in elements}.values() ) # print a mag_atoms_dists = unique_by_key(a, key=itemgetter(1)) # print (mag_atoms_dists) # a = unique_by_key(a, key=itemgetter(1)) print_and_log( 'I change spin for the following atoms:\ni atom dist\n', np.round(mag_atoms_dists, 2) , imp = 'y' ) # print 'I have found closest Fe atoms' muls = [(1.2, 0.6), (0.6, 1.2)] mag_moments_variants = [] for mm in muls: mags = copy.deepcopy(cl_test.init.magmom) # print mags for a, m in zip(mag_atoms_dists, mm): # print t[1] mags[a[0]] = mags[a[0]]*m mag_moments_variants.append(mags) print_and_log( 'The list of possible mag_moments:', imp = 'y' ) for i, mag in enumerate(mag_moments_variants): print_and_log( i, mag) print_and_log( 'Please use *mag_config* arg to choose desired config' , imp = 'y' ) if mag_config != None: st1.magmom = copy.deepcopy(mag_moments_variants[mag_config]) st2.magmom = copy.deepcopy(mag_moments_variants[mag_config]) name_suffix+='m'+str(mag_config) print_and_log('You have chosen mag configuration #',mag_config,imp = 'y') else: print_and_log('Non-magnetic calculation continue ...') """3. Add to struct_des, create geo files, check set, add_loop """ if starting_calc: it = starting_calc.id[0] it_new = it+'v'+str(starting_calc.id[2])+'.'+name_suffix if not it_new_folder: it_new_folder = struct_des[it].sfolder+'/neb/' obtained_from = str(starting_calc.id) if not ise_new: print_and_log('I will run add_loop() using the same set', important = 'Y') ise_new = cl.id[1] elif st: if not it_new: printlog('Error! please provide *it_new* - name for your calculation', important = 'Y') it = None it_new+='.'+name_suffix obtained_from = st.name if not ise_new: printlog('Error! please provide *ise_new*', important = 'Y') if not it_new_folder: printlog('Error! please provide *it_new_folder* - folder for your new calculation', important = 'Y') if it_new not in struct_des: add_des(struct_des, it_new, it_new_folder, 'Automatically created and added from '+obtained_from ) print_and_log('Creating geo files for starting and final configurations (versions 1 and 2) ', important = 'y') # if starting_calc: # cl = copy.deepcopy(starting_calc) # else: cl = CalculationVasp() #write start position struct_des[it_new].x_m_ion_start = x_m struct_des[it_new].xr_m_ion_start = xcart2xred([x_m], st1.rprimd)[0] cl.end = st1 ver_new = 1 cl.version = ver_new cl.path["input_geo"] = header.geo_folder + struct_des[it_new].sfolder + '/' + \ it_new+"/"+it_new+'.auto_created_starting_position_for_neb_'+search_type+'.'+str(ver_new)+'.'+'geo' cl.write_siman_geo(geotype = 'end', description = 'Starting conf. for neb from '+obtained_from, override = True) #write final position struct_des[it_new].x_m_ion_final = x_del struct_des[it_new].xr_m_ion_final = xcart2xred([x_del], st2.rprimd)[0] cl.end = st2 ver_new = 2 cl.version = ver_new cl.path["input_geo"] = header.geo_folder + struct_des[it_new].sfolder + '/' + \ it_new+"/"+it_new+'.auto_created_final_position_for_neb_'+search_type+'.'+str(ver_new)+'.'+'geo' cl.write_siman_geo(geotype = 'end', description = 'Final conf. for neb from '+obtained_from, override = True) #prepare calculations #Check if nebmake is avail # if int(runBash('ssh '+cluster_address+' test -e '+project_path_cluster+'/tools/vts/nebmake.pl; echo $?') ): # '' # print_and_log('Please upload vtsttools to ',cluster_address, project_path_cluster+'/tools/vts/') # raise RuntimeError # copy_to_server(path_to_wrapper+'/vtstscripts/nebmake.pl', to = project_path_cluster+'/tools/', addr = cluster_address) # if int(runBash('ssh '+cluster_address+' test -e '+project_path_cluster+'/tools/Vasp.pm; echo $?') ): # copy_to_server(path_to_wrapper+'/vtstscripts/Vasp.pm', to = project_path_cluster+'/tools/', addr = cluster_address) inherit_ngkpt(it_new, it, varset[ise_new]) add_loop(it_new, ise_new, verlist = [1,2], up = up, calc_method = calc_method, savefile = 'ov', inherit_option = inherit_option, n_neb_images = images, corenum = corenum, run =run ) if upload_vts: siman_dir = os.path.dirname(__file__) # print(upload_vts) push_to_server([siman_dir+'/cluster_tools/nebmake.pl', siman_dir+'/cluster_tools/Vasp.pm'], to = header.cluster_home+'/tools/vts', addr = header.cluster_address) else: print_and_log('Please be sure that vtsttools are at',header.cluster_address, header.cluster_home+'/tools/vts/', imp = 'Y') return it_new
def write_xyz(st=None, path=None, filename=None, file_name=None, include_vectors=True, repeat=1, shift_2view=1.0, replications=None, full_cell=False, analysis=None, show_around=None, show_around_x=None, nnumber=6, only_elements=None, gbpos2=None, gbwidth=1, withgb=False, include_boundary=2, imp_positions=[], imp_sub_positions=None, jmol=None, specialcommand=None, jmol_args=None, sts=None): """Writes st structure in xyz format in the folder xyz/path if repeat == 2: produces jmol script shift_2view - in rprimd[1][1] - shift of the second view gbpos2 - position of grain boundary in A gbwidth - atoms aroung gbpos2 will be colored differently imp_positions - (x1,x2,x3, element, label)- xcart and element name coordinates additionally to be added to structure; to visulaze all impurity positions: for jmol, additional key 's', 'i' can be added after element imp_sub_positions - list of atom numbers; the typat of these atoms is changed: not used now analysis - additional processing, allows to show only specifice atoms, 'imp_surrounding' - shows Ti atoms only around impurity nnumber - number of neighbours to show show_around - choose atom number around which to show, from 1 show_around_x - show atoms around point, has higher priority only_elements - see local_surrounding replications - list of replications, (2,2,2) full_cell - returns atoms to cell and replicate boundary atoms include_vectors (bool) - write primitive vectors to xyz jmol - 1,0 - use jmol to produce png picture jmol_args - see write_jmol() specialcommand - any command at the end of jmol script sts - list of Structure - write several structures to xyz file - other options are not working in this regime """ if jmol_args == None: jmol_args = {} if st == None: st = sts[0] if replications: st = replic(st, mul=replications, inv=1) def update_var(st): if st.natom != len(st.xred) != len(st.xcart) != len(st.typat) or len( st.znucl) != max(st.typat): printlog("Error! write_xyz: check your arrays.\n\n") if st.xcart == [] or len(st.xcart) != len(st.xred): printlog( "Warining! write_xyz: len(xcart) != len(xred) making xcart from xred.\n" ) st.xcart = xred2xcart(st.xred, st.rprimd) #print xcart[1] return st.rprimd, st.xcart, st.xred, st.typat, st.znucl, len(st.xred) rprimd, xcart, xred, typat, znucl, natom = update_var(st) if file_name: name = file_name elif filename: name = filename else: name = st.name if sts: name += '_traj' printlog("write_xyz(): Name is", name, important='n') if name == '': name = 'noname' if path: basepath = path else: basepath = 'xyz/' suf = '' """Processing section""" if analysis == 'imp_surrounding': if show_around == 0: printlog('Error! number of atom *show_around* should start from 1') suf = '_loc' + str(show_around) lxcart = [] ltypat = [] i = 0 if is_list_like(show_around_x): x = show_around_x x_t = local_surrounding(x, st, nnumber, control='atoms', periodic=True, only_elements=only_elements) # print('write_xyz: local_surround:', x_t) lxcart += x_t[0] ltypat += x_t[1] else: for t, x in zip(typat, xcart): condition = False # print show_around, 'show' if show_around: # print i, condition condition = (i + 1 == show_around) # print i, condition else: condition = ( t > 1 ) # compat with prev behav, to show around any impurities (all atoms with typat more than one) # print 'se', condition if condition: # lxcart.append(x) # ltypat.append(t) # print x, ' x' x_t = local_surrounding(x, st, nnumber, control='atoms', periodic=True, only_elements=only_elements) # print x_t[1] lxcart += x_t[0] ltypat += x_t[1] i += 1 xcart = lxcart typat = ltypat natom = len(typat) # print natom, 'nat' name += suf xyzfile = os.path.join(basepath, name + ".xyz") makedir(xyzfile) """Include atoms on the edge of cell""" if full_cell: # print xred # print natom # st = return_atoms_to_cell(st) # print xred st = replic(st, mul=(1, 1, 2), inv=0, cut_one_cell=1, include_boundary=include_boundary) # print natom, st.natom # print st.xred rprimd, xcart, xred, typat, znucl, natom = update_var(st) # asdegf """Writing section""" # printlog("Writing xyz: "+xyzfile, imp = 'y') #analyze imp_positions if imp_sub_positions == None: imp_sub_positions = [] nsub = 0 for pos in imp_positions: # if len(pos) > 4: # if 's' not in pos[4]: continue # skip interstitial positions xs = np.asarray([pos[0], pos[1], pos[2]]) nsub += 1 # print xs for i, x in enumerate(xcart): # print np.linalg.norm( x-xs) if np.linalg.norm(x - xs) < 1: imp_sub_positions.append(i) if imp_sub_positions: printlog(imp_sub_positions, ': numbers of found atoms to be changed ') # for i in sorted(indices, reverse=True): # del somelist[i] if include_vectors: nvect = 3 else: nvect = 0 def write(st): rprimd, xcart, xred, typat, znucl, natom = update_var(st) f.write(str(natom + len(imp_positions) - nsub + nvect) + "\n") #+3 vectors f.write(name + "\n") if imp_positions: for i, el in enumerate(imp_positions): # if len(el) != 4: continue f.write("%s %.5f %.5f %.5f \n" % (el[3], el[0], el[1], el[2])) # print 'composite -pointsize 60 label:{0:d} -geometry +{1:d}+{2:d} 1.png 2.png'.format(i, el[0], el[1]) for i in range(natom): typ = typat[i] - 1 z = int(znucl[typ]) if i in imp_sub_positions: # f.write( "Be " ) continue else: el = element_name_inv(z) f.write(el + " ") f.write("%.5f %.5f %.5f \n" % (xcart[i][0], xcart[i][1], xcart[i][2])) if include_vectors: for r in st.rprimd: f.write('Tv {:.10f} {:.10f} {:.10f}\n'.format(*r)) with open(xyzfile, 'w') as f: if sts: for st in sts: write(st) else: for i in range(repeat): write(st) # os._exit(1) printlog('File', xyzfile, 'was written', imp='y') pngfile = None if jmol: """ script mode for jmol. Create script file as well for elobarate visualization """ """Choose gb atoms to change their color""" printlog('position of boundary 2', gbpos2) atomselection = '' #create consistent xcart_new list like it will be in Jmol xcart_new = [] for i, x in enumerate(xcart): if i in imp_sub_positions: continue xcart_new.append(x) if gbpos2: gbpos1 = gbpos2 - rprimd[0][0] / 2. gbatoms = [] for i, x in enumerate(xcart_new): # print i # if x[0] > gbpos1 - gbwidth/2. and x[0] < gbpos1 + gbwidth/2.: if abs(x[0] - gbpos1) < gbwidth / 2.: gbatoms.append(i) # print i, x[0], abs(x[0] - gbpos1) if abs(x[0] - gbpos2) < gbwidth / 2.: # if x[0] > gbpos2 - gbwidth/2. and x[0] < gbpos2 + gbwidth/2.: # print i, x[0], abs(x[0] - gbpos2) gbatoms.append(i) printlog('Atoms at GB:', gbatoms) atomselection = '' for i in gbatoms: atomselection += 'Ti' + str(i + 1 + len(imp_positions)) + ',' atomselection = atomselection[:-1] # elif withgb: # color half of cell # else: # color half of cell # # pass # atomselection = 'atomno>'+str(0+len(imp_positions) )+' and atomno<'+str(( natom + len(imp_positions) )/2-1) xyzfile = os.getcwd() + '/' + xyzfile scriptfile = basepath + name + ".jmol" bn = (basepath + name).replace('.', '_') pngfile = os.getcwd() + '/' + bn + ".png" printlog('imp_positions = ', imp_positions) write_jmol(xyzfile, pngfile, scriptfile, atomselection, rprimd=rprimd, shift=shift_2view, label=[(pos[3], pos[4]) for pos in imp_positions], specialcommand=specialcommand, **jmol_args) return xyzfile, pngfile