def write_local(cl, it_new, it_new_path, el_sub, main_path ): cl.version = v # it_new_path cl.name = it_new cl.des = 'Obtained from end state of '+str((it, ise, v))+' by substitution of one atom near gb with '+el_sub+' impurity ' path_new_geo = it_new_path+"/"+it_new+"/"+it_new+'.imp.'+el_sub+'.'+str(cl.version)+'.'+'geo' cl.init.name = it_new+".init."+str(cl.version) xyzpath = it_new_path+"/"+it_new cl.path["input_geo"] = path_new_geo print(path_new_geo) cl.write_geometry("init", cl.des, override = 1) write_xyz(cl.init, xyzpath) return it_new_path
def determine_voids(st, r_impurity, fine=1, step_dec=0.05): if not r_impurity: printlog('add_neb(): Error!, Please provide *r_impurity* (1.6 A?)') sums = [] avds = [] printlog('Searching for voids', important='y') st_pores = find_pores(st, r_matrix=0.5, r_impurity=r_impurity, step_dec=step_dec, fine=fine, calctype='all_pores') printlog('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') 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) summ, avd = local_surrounding2(x, st, n_neighbours=6, control='sum_av_dev', periodic=True) # print (summ, avd) 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') return st_pores, sums, avds
def insert(it_ins, ise_ins, mat_path, it_new, calc, type_of_insertion="xcart"): """For insertion of atoms to cells with changed lateral sizes Input: 'type_of_insertion = xred' used to add xred coordinates mat_path - path to geo files which are supposed to be changed it_ins - already existed calculation; xred will be used from this calculation. it_new - new folder in geo folder for obtained structure This function finds version of calculation in folder mat_path and tries to use the same version of it_ins """ if not os.path.exists(mat_path): print_and_log("Error! Path " + mat_path + " does not exist\n\n") raise RuntimeError if it_ins not in mat_path and it_ins not in it_new: print_and_log('Cells are', it_ins, mat_path, it_new) print_and_log( "Error! you are trying to insert coordinates from cell with different name\n\n" ) #raise RuntimeError hstring = ("%s #on %s" % (traceback.extract_stack(None, 2)[0][3], datetime.date.today())) if hstring != header.history[-1]: header.history.append(hstring) geofilelist = runBash('find ' + mat_path + '/target -name "*.geo*" ').splitlines() if geofilelist == []: print_and_log( "Warning! Target folder is empty. Trying to find in root folder ..." ) geofilelist = runBash('find ' + mat_path + '/ -name "*.geo*" ').splitlines() ins = None for mat_geofile in geofilelist: mat = CalculationVasp() mat.name = mat_geofile mat.read_geometry(mat_geofile) #step = 0.27 #r_pore = 0.56 #r_mat = mat.hex_a / 2 - step #pores = find_pores(mat.init, r_mat, r_pore, step, 0.3, 'central') #octahedral #mat.xcart.append ( pores.xcart[0] ) #mat.typat.append(1) try: ins_working = ins ins = calc[(it_ins, ise_ins, mat.version)] except KeyError: print_and_log("No key", (it_ins, ise_ins, mat.version), "I use previous working version !!!", imp='y') ins = ins_working #return #ins.end.znucl = ins.znucl #ins.end.nznucl = ins.nznucl #ins.end.ntypat = ins.ntypat #ins.end.typat = ins.typat #print ins.xcart[-1] mat_geopath = geo_folder + struct_des[it_new].sfolder + '/' if type_of_insertion == "xcart": #Please update here! mat_filename = '/' + it_new + "." + "inserted." + str( mat.version) + '.' + 'geo' v = np.zeros(3) result = insert_cluster(ins.end, v, mat.init, v) mat.end = result mat.init = result # mat.znucl = mat.end.znucl # mat.nznucl = mat.end.nznucl # mat.ntypat = mat.end.ntypat # mat.typat = mat.end.typat # mat.natom = len(mat.end.xred) #mat.version = ins.version des = ins.name + " was inserted to " + mat_geofile elif type_of_insertion == "xred": mat_filename = '/from/' + it_new + ".xred." + str( mat.version) + '.' + 'geo' #mat.end.rprimd = mat.rprimd #mat.init.xred = copy.deepcopy(ins.end.xred) #mat.init.typat = copy.deepcopy(ins.end.) #print ins.end.xcart rprimd = copy.deepcopy(mat.init.rprimd) #build = mat.build mat.init = copy.deepcopy(ins.end) #mat.build = build mat.init.rprimd = rprimd #return initial rprimd mat.init.xred2xcart() #calculate xcart with new rprimd des = "atoms with reduced coord. from " + ins.name + " was fully copied to " + mat_geofile mat.init.name = 'test_insert_xred' + str(mat.version) write_xyz(mat.init) mat.path["input_geo"] = mat_geopath + it_new + mat_filename if not mat.write_geometry("init", des): continue print_and_log("Xred from " + it_ins + " was inserted in " + mat_geofile + " and saved as " + mat_filename + " \n\n") return
def add(znucl, xyzpath="", new=None, write_geo=True, put_exactly_to=None): "if put_exactly_to is True, then atom just added and nothing are searched" if write_geo and os.path.exists( new.path["input_geo"]) and not override: print_and_log("add: File '" + new.path["input_geo"] + "' already exists; continue\n", imp='Y') return new #new.init = return_atoms_to_cell(new.init) if replace_atom: #atom substitution if znucl not in new.init.znucl: new.init.znucl.append(znucl) new.init.ntypat += 1 new.init.typat[replace_atom] = new.init.ntypat else: ind = new.init.znucl.index(znucl) new.init.typat[replace_atom] = ind + 1 new.init.nznucl = [] for typ in range(1, new.init.ntypat + 1): new.init.nznucl.append(new.init.typat.count(typ)) else: new_before = copy.deepcopy(new) # new.init.xcart[-2][0]-=0.9 #was made once manually for c1gCOi10.1 # new.init.xcart[-2][2]+=0.2 # new.init.xred = xcart2xred(new.init.xcart, new.init.rprimd) write_xyz(new.init) #step = 0.042 step = 0.06 #r_pore = 0.56 #fine = 0.3 # for visualisation of pores #fine = 4 #controls small steps; the steps are smaller for larger numbers #r_pore = 0.54 prec = 0.004 # precision of center Angs if new.hex_a == None: r_mat = 1.48 - step else: r_mat = new.hex_a / 2 - step if put_exactly_to: pores_xred = [ np.array(put_exactly_to), ] print_and_log('Inmpurity just put in ', pores_xred, imp='Y') else: pores = find_pores(new.init, r_mat, r_pore, step, fine, prec, addtype, new.gbpos, find_close_to, check_pore_vol) #octahedral pores_xred = pores.xred npores = len(pores_xred) st = new.init #delete last oxygen; was made once manually for c1gCOi10.1 # st.natom-=1 # del st.xred[-1] # del st.typat[-1] st.natom += npores st.xred.extend(pores_xred) if znucl in st.znucl: print_and_log("znucl of added impurity is already in cell") ind = st.znucl.index(znucl) typat = ind + 1 st.nznucl[ind] += npores else: st.ntypat += 1 typat = st.ntypat st.znucl.append(znucl) st.nznucl.append(npores) for i in range(npores): st.typat.append(typat) st.xred2xcart() new.init = st #print "Add impurity: len(xred ", len(new.init.xred) #print "natom", new.init.natom #For automatisation of fit try: #new.build if new.build.nadded == None: new.build.nadded = npores else: new.build.nadded += npores if new.build.listadded == [None]: new.build.listadded = range( new.natom - npores, new.natom) #list of atoms which were added else: new.build.listadded.extend( range(new.natom - npores, new.natom)) #print "Warning!!! Information about added impurities rewritten" except AttributeError: pass #new.init.znucl = new.znucl #new.init.typat = new.typat #write_xyz(replic(new.init, (2,1,2)) , xyzpath) #test_adding_of_impurities(new, new_before, v) print_and_log("Impurity with Z=" + str(znucl) + " has been added to the found pore in " + new.name + "\n\n") if write_geo: write_xyz(new.init, xyzpath) new.write_geometry("init", new.des, override=override) print_and_log("\n") return new
def neb_analysis(cl, show, up=None, push2archive=None, old_behaviour=None, results_dic=None, fitplot_args=None, style_dic=None, params=None): """ Analyse traectories and polarons params mep_shift_vector """ def determing_rms_for_surrounding_atoms(sts): # change of rms on each step compared to first structure #here first and last structures should correspond to first and last images st1 = sts[0] st_interp = interpolate(sts[0], sts[-1], 1)[0] rms_list = [] for st in sts: rms = rms_pos_diff(st_interp, st) rms_list.append(rms) print('rms is {:.3f}'.format(rms)) print('d rms is {:.3f}'.format(abs(rms_list[3] - rms_list[0]))) rms_change = abs(min(rms_list) - max(rms_list)) return rms_change def determing_born_barrier(sts): #here first and last structures should correspond to first and last images local_born_e = [] i = find_moving_atom(sts[0], sts[-1]) for st in sts: local_born_e.append(site_repulsive_e(st, i)) # import matplotlib.pyplot as plt # plt.plot(local_born_e) # plt.show() return abs(min(local_born_e) - max(local_born_e)) if params is None: params = {} if results_dic is None: results_dic = {} calc = header.calc path2mep_s = cl.project_path_cluster + '/' + cl.dir + '/mep.eps' itise = cl.id[0] + '.' + cl.id[1] # print(cl.ldauu) # sys.exit() name_without_ext = 'mep.' + itise + '.U' + str(max(cl.ldauu)) path2mep_l = cl.dir + name_without_ext + '.eps' # print(path2mep_l) if not os.path.exists(path2mep_l) or '2' in up: '' get_from_server( files=path2mep_s, to_file=path2mep_l, addr=cl.cluster_address, ) movie_to = cl.dir + '/movie.xyz' get_from_server( files=cl.project_path_cluster + '/' + cl.dir + '/movie.xyz', to_file=movie_to, addr=cl.cluster_address, ) if os.path.exists(movie_to): makedir('figs/' + name_without_ext + '.xyz') shutil.copyfile(movie_to, 'figs/' + name_without_ext + '.xyz') # trying to get one image closest to the saddle point if old_behaviour and cl.version == 2: #old behaviour, now created automatically in add callc im = cl.set.vasp_params['IMAGES'] # if im % 2 > 0: #odd # i = im//2 + 1 # else: # i = im/2 # if choose_image: # i = choose_image for i in range(im): i += 1 cl_i = copy.deepcopy(cl) cl_i.version += i cl_i.id = (cl.id[0], cl.id[1], cl_i.version) cl_i.name = str(cl_i.id[0]) + '.' + str(cl_i.id[1]) + '.' + str( cl_i.id[2]) # print cl_i.name cl_i.path["output"] = cl_i.dir + '0' + str(i) + "/OUTCAR" # for i in range(): cl_i.associated_outcars = [ aso[2:] for aso in cl_i.associated_outcars ] # print cl_i.path["output"] cl_i.state = '2. Ready to read outcar' # if not os.path.exists(cl_i.path["output"]): # load = 'o' outst2 = ("%s" % cl_i.name).ljust(name_field_length) if readfiles: print(outst2 + '|' + cl_i.read_results( loadflag, show=show, choose_outcar=choose_outcar)) else: print_and_log(outst2 + ' | File was not read') if cl_i.id in calc: #move creation of calcs with images to add_neb '' # print_and_log('Please test code below this message to save prev calcs') # if cl_i != calc[cl_i.id] # if hasattr(calc[cl_i.id], 'prev') and calc[cl_i.id].prev: # prevlist = calc[cl_i.id].prev # else: # prevlist = [calc[cl_i.id]] # cl_i.prev = prevlist # calc[cl_i.id] = cl_i else: calc[cl_i.id] = cl_i # print path2mep_l if 0: if os.path.exists(path2mep_l): # get_from_server(file = path2mep_s, to = path2mep_l, addr = cluster_address) runBash('evince ' + path2mep_l) else: a = glob.glob(cl.dir + '*mep*') if a: runBash('evince ' + a[0]) cl1 = calc[cl.id[0], cl.id[1], 1] cl2 = calc[cl.id[0], cl.id[1], 2] atom_num = find_moving_atom(cl1.end, cl2.end) # cl1.poscar() # cl2.poscar() # print('atom_num',atom_num) # sys.exit() #prepare lists ni = cl.set.vasp_params['IMAGES'] vlist = [1] + list(range(3, ni + 3)) + [2] # print( vlist) mep_energies = [] atom_pos = [] pols = [] sts = [] sts_loc = [] dAO2 = [] # A-(O,F) distance for each image dAO4 = [] # A-(O,F) distance for each image dAO6 = [] dAO6harm = [] dAO6dev = [] for v in vlist: cli = calc[cl.id[0], cl.id[1], v] # if v == 1: # cli = db['NaVP2O7_a.su.s101015v100.n5Na1v1ms.ifn.1mls.1'] # print(cl.id[0], cl.id[1], v, cli.state) if '4' not in cli.state and 'un' not in up: printlog('Attention! res_loop(): analys_type == neb, Calc', cli.id, 'is not finished; return') return {}, [] # print cli.id # cli.end = return_to_cell(cli.end) # mep_energies.append( min(cli.list_e_sigma0) ) #use minimum energy - not very good, sometimes unconverged energy could be lower! e0 = cli.energy_sigma0 if params and params.get( 'neb_penult_e' ): # allows to take e from the previous relaxation step in case the calculation was aborted e0 = cli.list_e_sigma0[-2] mep_energies.append(e0) #use last energy atom_pos.append(cli.end.xcart[atom_num]) # Find polaron positions if 'polaron' in show: # if 1 cause error for nomag calc pol, mag = find_polaron(cli.end, atom_num) if pol: for key in pol: if np.any(pol[key]): for n in pol[key]: if n not in pols: pols.append(n) else: '' # print('Mag_moments on trans,', mag.round(1)) if 0 or 'neb_geo' in show: #visualization of path # print(atom_num) st = copy.deepcopy(cli.end) # print('moving_atom', st.xcart[atom_num]) info = st.nn(atom_num, 15, from_one=False, silent=1) st.moving_atom_i = atom_num st_loc = info['st'] # print(st_loc.xcart) # st_loc = st_loc.shift if v == vlist[0]: st1 = copy.deepcopy(st) vec = st.center_on(atom_num) # vec = np.asarray([0.,0.,0.]) if params is not None and 'mep_shift_vector' in params: # vec += np.array([0.11,0.11,0]) # path4 # print(params['mep_shift_vector']) vec += np.array(params['mep_shift_vector']) # path4 # print(vec) st_loc = st_loc.shift_atoms(vec) if 0: st_loc.write_xyz() # st.write_cif('xyz/'+st.name) if 0: st.shift_atoms(vec).write_xyz() sts_loc.append(st_loc) st1 = st1.add_atom(st.xred[atom_num], 'Rb') sts.append(st.shift_atoms(vec)) if 0 or 'neb_geo2' in show: printlog('\n\nVersion {:}:'.format(v), imp='y') info1 = st.nn(atom_num, 2, from_one=False, silent=1, more_info=1) print('Av. dist A-2(O,F) {:.3f} A'.format( info1['av(A-O,F)'])) # print('Av. squared dist A-2(O,F) {:.3f} A'.format(info1['avsq(A-O,F)'])) dAO2.append(info1['av(A-O,F)']) info12 = st.nn(atom_num, 3, from_one=False, silent=1) print('Average distance A-3(O,F) {:.2f} A'.format( info12['av(A-O,F)'])) info2 = st.nn(atom_num, 4, from_one=False, silent=1) print('Average distance A-4(O,F) {:.2f} A'.format( info2['av(A-O,F)'])) dAO4.append(info2['av(A-O,F)']) info3 = st.nn(atom_num, 6, from_one=False, silent=1, more_info=1) print('Average_distance A-6(O,F) {:.2f} A '.format( info3['av(A-O,F)'])) print('Av. harm. dist A-6(O,F) {:.2f} A'.format( info3['avharm(A-O,F)'])) print('Average_deviation A-6(O,F) {:.1f} mA'.format( info3['avdev(A-O,F)'])) dAO6.append(info3['av(A-O,F)']) dAO6dev.append(info3['avdev(A-O,F)']) dAO6harm.append(info3['avharm(A-O,F)']) if 'neb_rms' in show: rms_change = determing_rms_for_surrounding_atoms(sts) results_dic['rms_change'] = rms_change if 'neb_born' in show: results_dic['born_barrier'] = determing_born_barrier(sts) print('Born barrier is {:.2f} eV '.format(results_dic['born_barrier'])) # print(results_dic['rms_change']) # print('show is', show) # sys.exit() # print('flag ', 'neb_noxyz' not in show, show) if 'neb_noxyz' not in show and sts: write_xyz(sts=sts) # write traectory write_xyz(sts=sts_loc) # write traectory if 'jmol' in params: write_xyz(sts=sts, jmol=1, jmol_args=params['jmol']) # write jmol st1 = st1.shift_atoms(vec) st1.name += '_all' # st1.write_cif('xyz/'+st1.name) st1.write_xyz() if dAO2: # find maximum change of distance during migration dAO2_change = abs(min(dAO2) - max(dAO2)) results_dic['dAO2_change'] = dAO2_change if dAO4: # find maximum change of distance during migration dAO4_change = abs(min(dAO4) - max(dAO4)) results_dic['dAO4_change'] = dAO4_change if dAO6: # dAO6_change = abs(min(dAO6) - max(dAO6)) results_dic['dAO6_change'] = dAO6_change if dAO6harm: # results_dic['dAO6harm_change'] = abs(min(dAO6harm) - max(dAO6harm)) if dAO6dev: # results_dic['dAO6dev_change'] = abs(min(dAO6dev) - max(dAO6dev)) results_dic[ 'sts_loc'] = sts_loc # list of local structures, each structure contains dlist - distances from central cation to anions, and ellist - types of elements results_dic[ 'sts'] = sts # list of mep structures, each structure contains moving_atom_i - number of moving atom if len(pols) > 0: print( 'During migration of alkali ions polarons are detected on atoms:', pols) elif len(pols) > 1: printlog( 'Attention! polaron is moving during migration! Obtained barrier is ambiguous' ) else: printlog( 'Compare magnetic moments above! In principle should be the same!') # print np.array(atom_pos) #test if the distances between points are not spoiled by PBC nbc = range(-1, 2) jj = 0 for x in atom_pos: x2 = atom_pos[jj + 1] # x = np.array(x) # x2 = np.array(x2) r = cl.end.rprimd d1, _ = image_distance(x, x2, r, order=1) #minimal distance x2_gen = (x2 + (r[0] * i + r[1] * j + r[2] * k) for i in nbc for j in nbc for k in nbc) #generator over PBC images x2c = copy.deepcopy(x2) ii = 0 while np.linalg.norm(x - x2c) > d1: #find the closest PBC image position if ii > 100: break ii += 1 x2c = next(x2_gen) atom_pos[jj + 1] = x2c jj += 1 if jj == len( atom_pos ) - 1: # the last point is not needed, we could not use slice since we need to use changed atom_pos in place break # print np.linalg.norm(x - x2c), d1 _, diff_barrier = plot_mep(atom_pos, mep_energies, plot=0, show=0, fitplot_args=fitplot_args, style_dic=style_dic) results_dic['barrier'] = diff_barrier middle_image = len(vlist) // 2 results_dic['dEm1'] = mep_energies[middle_image] - mep_energies[0] cl1.barrier = diff_barrier cl2.barrier = diff_barrier results_dic['atom_pos'] = [list(pos) for pos in atom_pos] results_dic['mep_energies'] = mep_energies if 'mep' in show: if 'mepp' in show: show_flag = True else: show_flag = False # sys.exit() plot_mep(atom_pos, mep_energies, image_name='figs/' + name_without_ext + '_my.eps', show=show_flag, fitplot_args=fitplot_args, style_dic=style_dic) if push2archive: path2saved, _ = plot_mep(atom_pos, mep_energies, image_name='figs/' + name_without_ext + '_my', fitplot_args=fitplot_args, style_dic=style_dic) push_figure_to_archive(local_figure_path=path2saved, caption=description_for_archive) if 0: #copy files according to chosen outcar to run nebresults locally wd = cl_i.dir out_i = cl_i.associated_outcars[choose_outcar - 1] out_1 = calc[cl.id[0], cl.id[1], 1].associated_outcars[choose_outcar - 1] out_2 = calc[cl.id[0], cl.id[1], 2].associated_outcars[choose_outcar - 1] # print out_1 # print out_2 shutil.copyfile(wd + out_1, wd + '00/OUTCAR') shutil.copyfile(wd + out_2, wd + '04/OUTCAR') for d in ['01/', '02/', '03/']: shutil.copyfile(wd + d + out_i, wd + d + 'OUTCAR') # print wd+d+out_i return results_dic
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, 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, it_folder=None, inherit_magmom=False, x_start=None, xr_start=None, x_final=None, xr_final=None, upload_vts=False, center_on_moving=True, run=False, add_loop_dic=None, old_behaviour=None, params=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 or it_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 - old_behaviour (str) - choose naming behavior before some date in the past for compatibility with your projects '020917' '261018' - after this moment new namig convention applied if end_pos_types_z is used - add_loop_dic - standart parameters of add() - params (dic) - provide additional parameters to add() # should be removed ###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) """ naming_conventions209 = True # set False to reproduce old behavior before 2.09.2017 if old_behaviour == '020917': naming_conventions209 = False # # print('atom_to_insert', 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 = [] end_pos_types_z = sorted(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*') corenum = add_loop_dic.get('corenum') # print(corenum) # sys.exit() 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(corenum) # sys.exit() # 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] # print('atom_to_insert', atom_to_insert) # sys.exit() 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) final_pos_z = end_pos_types_z or [ invert(type_atom_to_move) ] # by default only moving atom is considered end_pos_types_el = [invert(z) for z in end_pos_types_z] sur = local_surrounding(x_m, st, n_neighbours=14, control='atoms', only_elements=final_pos_z, periodic=True) #exclude the atom itself # print(x_m) # print(sur) # st.nn() end_pos_n = sur[2][1:] print_and_log( 'I can suggest you ' + str(len(end_pos_n)) + ' end positions. The distances to them are : ', np.round(sur[3][1:], 2), ' A\n ', 'They are ', [invert(z) for z in final_pos_z], 'atoms, use *i_void_final* to choose required: 1, 2, 3 ..', imp='y') i_sym_final_l = [] for j in end_pos_n: for i, l in enumerate(numbers): if j in l: i_sym_final_l.append(i + 1) printlog('Their symmetry positions are ', i_sym_final_l, imp='y') # sys.exit() if not i_void_final: printlog('Changing i_void_final: None -> 1', imp='y') i_void_final = 1 #since zero is itself chosen_dist = sur[3][i_void_final] print_and_log('Choosing position ', i_void_final, 'with distance', round(chosen_dist, 2), 'A', imp='y') # print(end_pos_n) i_sym_final = 0 n_final = sur[2][i_void_final] for i, l in enumerate(numbers): if n_final in l: i_sym_final = i + 1 printlog('It is symmetrically non-equiv position #', i_sym_final, imp='y') # sys.exit() header.temp_chosen_dist = chosen_dist if old_behaviour == '261018': name_suffix += el_num_suffix + 'v' + str(i_void_final) else: name_suffix += el_num_suffix + 'v' + str( i_void_final) + list2string(end_pos_types_el, joiner='') # print(name_suffix) # sys.exit() 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 or 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 '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 if search_type != None: # for None not implemented; x_m should be determined first for this #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 and not it_folder: printlog( 'Error! please provide *it_new_folder* - folder for your new calculation', important='Y') if it_folder: it_new_folder = it_folder 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 if search_type is not None: 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() print('Trying to find x_m', x_m) i1 = st1.find_atom_num_by_xcart( x_m, prec=0.45, ) # sys.exit() print('Trying to find x_del', x_del) i2 = st2.find_atom_num_by_xcart( x_del, prec=0.45, ) 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.45) # the positions were changed # check if this is correct i2 = st2.find_atom_num_by_xcart(x_del, prec=0.45) 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 and search_type is not None: st1s = st1.replace_atoms([i1], 'Pu') st2s = st2.replace_atoms([i2], 'Pu') else: st1s = copy.deepcopy(st1) st2s = copy.deepcopy(st2) if center_on_moving and search_type is not None: 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') print(a) 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]) if run: add_loop_dic['run'] = run add_loop_dic['corenum'] = corenum # print(add_loop_dic) 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, # params=params, **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 write_geometry_files(dlist, in_calc, xcart_pores, segtyp, take_final_rprimd_from=None, add_name_before='', tlist=[], configver=False, add_typat=None): """Creating files dlist - list of pairs with distances and numbers in_calc - base calculation without pores tlist - list of additional atoms in the case of triples; list of structures configver - if True each configuration saved as a new version add_typat - manually determined; please automatize! """ print(("Warning! add_typat", add_typat)) if tlist == []: #convert dlist to tlist - to do earlier for el in dlist: config = Structure() config.name = el[2] config.length = el[0] config.typat = add_typat config.xcart = [el[7], el[8]] tlist.append(config) for i, el in enumerate(tlist): #by all found structures print(("el name is ", el.name)) stn = copy.deepcopy(in_calc.init) calc = copy.deepcopy(in_calc) stn.typat.extend(el.typat) stn.xcart.extend(el.xcart) stn.xred = xcart2xred(stn.xcart, stn.rprimd) xcart_check = xred2xcart(stn.xred, stn.rprimd) assert len(xcart_check) == len(stn.xcart) #test assert all( [ all(np.around(v1, 8) == np.around(v2, 8)) for (v1, v2) in zip(stn.xcart, xcart_check) ] ) #check if xcart2xred(stn.xcart,r) and xred2xcart(stn.xred,r) are working correctly up to the eight digits after stn.natom = len(stn.xcart) """Adapt new rprimd""" print("take final rprimd is ", take_final_rprimd_from) if take_final_rprimd_from: #read final rprimd and version print("Start to read rprimd and version from " + take_final_rprimd_from) in_calc_rprimd = CalculationVasp() in_calc_rprimd.name = 'temp' in_calc_rprimd.read_geometry(take_final_rprimd_from) stn.rprimd = in_calc_rprimd.init.rprimd stn.xcart = xred2xcart(stn.xred, stn.rprimd) calc.version = in_calc_rprimd.version elif configver: calc.version = i + 1 calc.init = stn des = ' was obtained by the insertion of C-O pair into ' + in_calc_name + '; final vectors taken from corresponding ver' calc.build.ipairlen = el.length # Initial length of pair if not hasattr(calc.build, 'nadded') or calc.build.nadded == None: calc.build.nadded = 2 else: calc.build.nadded += 2 if not hasattr(calc.build, 'listadded') or calc.build.listadded == [None]: calc.build.listadded = list(range( stn.natom - 2, stn.natom)) #list of atoms which were added else: calc.build.listadded.extend( list(range(stn.natom - 2, stn.natom))) structure_name = calc.name + el.name.split('.')[0] #calc.name = add_name_before+calc.name+ '.' +el[2]+'.'+str(calc.version) print('Structure_name', structure_name) if structure_name in struct_des: if configver: fname = structure_name # calc.name+'C2O2' calc.path["input_geo"] = geo_folder + struct_des[ fname].sfolder + '/' + fname + '/' + structure_name + '.' + segtyp + '.' + str( calc.version) + '.geo' else: calc.path["input_geo"] = geo_folder + struct_des[ structure_name].sfolder + '/' + structure_name + '/' + structure_name + '.' + segtyp + '.' + str( calc.version) + '.geo' print("write geo to ", calc.path["input_geo"]) calc.write_geometry('init', des) print("write_geometry_files(): name ", el.name) stn.name = add_name_before + calc.name + '' + str( el.name) + '.' + str(calc.version) #stn = replic(stn, (1,2,2)) write_xyz(stn) print("__________________________\n\n\n") return
def find_pairs(base_name, segtyp, in_calc, central_atoms=[], xcart1imp=None, input_dlist_coseg=None, prec=2, gvolume_config_num=None, gbpos=None, take_final_rprimd_from=None, main_path=None, based_on=None, target_znucl=[22, 6, 8], max_dist_between_atoms=4.8, add_typat=[2, 3]): """ Find uniq pairs of atoms and analyse them Input: segtyp - three regimes for cells with grain boundaries: 'segreg' assumes that in_calc contains carbon atom in grain volume, and creates all cases; 'coseg' assumes pure cell and creates only coseg cases. cosegregation cases of course should be the same for two regimes, however co-segregation configuations after 'coseg' is more easy to relax. 'grainvol' - searching for pairs in grain volume two regimes for bulk cells: 'bulk_triple' - used for bulk cells without grain boundaries; first step is searching for pairs, second step for triples. 'bulk_pairs' - used for bulk cells without grain boundaries; searching for pairs. new_name - name of created structures; at first should be added to struct_des[] in_calc - Calculation() type or path to geo file region - list of numbers which determine region central_atoms - list of atoms for which pairs are constructed (Warinig! numbers in new array xcart_pores!); - parameter to change mode; xcart1imp - coordinates of first interstitial in the grain interior input_dlist_coseg - list of configurations with cosegregation cases. Needed to construct corresponding segregation cases. the format is quiet tricky prec - precision of lengths used to determine unique positions. gvolume_config_num - number of configuration with two atoms in grain volume choosen by user (usually should be the most favourable) gbpos - position of grain boundary take_final_rprimd_from - path to geo file from which rprimd will be used target_znucl - numbers of target atoms max_dist_between_atoms - now at least used for 'bulk_pairs' and 'bulk_triple'; maximum length of found pairs. add_typat - mannualy set please update """ def write_geometry_files(dlist, in_calc, xcart_pores, segtyp, take_final_rprimd_from=None, add_name_before='', tlist=[], configver=False, add_typat=None): """Creating files dlist - list of pairs with distances and numbers in_calc - base calculation without pores tlist - list of additional atoms in the case of triples; list of structures configver - if True each configuration saved as a new version add_typat - manually determined; please automatize! """ print(("Warning! add_typat", add_typat)) if tlist == []: #convert dlist to tlist - to do earlier for el in dlist: config = Structure() config.name = el[2] config.length = el[0] config.typat = add_typat config.xcart = [el[7], el[8]] tlist.append(config) for i, el in enumerate(tlist): #by all found structures print(("el name is ", el.name)) stn = copy.deepcopy(in_calc.init) calc = copy.deepcopy(in_calc) stn.typat.extend(el.typat) stn.xcart.extend(el.xcart) stn.xred = xcart2xred(stn.xcart, stn.rprimd) xcart_check = xred2xcart(stn.xred, stn.rprimd) assert len(xcart_check) == len(stn.xcart) #test assert all( [ all(np.around(v1, 8) == np.around(v2, 8)) for (v1, v2) in zip(stn.xcart, xcart_check) ] ) #check if xcart2xred(stn.xcart,r) and xred2xcart(stn.xred,r) are working correctly up to the eight digits after stn.natom = len(stn.xcart) """Adapt new rprimd""" print("take final rprimd is ", take_final_rprimd_from) if take_final_rprimd_from: #read final rprimd and version print("Start to read rprimd and version from " + take_final_rprimd_from) in_calc_rprimd = CalculationVasp() in_calc_rprimd.name = 'temp' in_calc_rprimd.read_geometry(take_final_rprimd_from) stn.rprimd = in_calc_rprimd.init.rprimd stn.xcart = xred2xcart(stn.xred, stn.rprimd) calc.version = in_calc_rprimd.version elif configver: calc.version = i + 1 calc.init = stn des = ' was obtained by the insertion of C-O pair into ' + in_calc_name + '; final vectors taken from corresponding ver' calc.build.ipairlen = el.length # Initial length of pair if not hasattr(calc.build, 'nadded') or calc.build.nadded == None: calc.build.nadded = 2 else: calc.build.nadded += 2 if not hasattr(calc.build, 'listadded') or calc.build.listadded == [None]: calc.build.listadded = list(range( stn.natom - 2, stn.natom)) #list of atoms which were added else: calc.build.listadded.extend( list(range(stn.natom - 2, stn.natom))) structure_name = calc.name + el.name.split('.')[0] #calc.name = add_name_before+calc.name+ '.' +el[2]+'.'+str(calc.version) print('Structure_name', structure_name) if structure_name in struct_des: if configver: fname = structure_name # calc.name+'C2O2' calc.path["input_geo"] = geo_folder + struct_des[ fname].sfolder + '/' + fname + '/' + structure_name + '.' + segtyp + '.' + str( calc.version) + '.geo' else: calc.path["input_geo"] = geo_folder + struct_des[ structure_name].sfolder + '/' + structure_name + '/' + structure_name + '.' + segtyp + '.' + str( calc.version) + '.geo' print("write geo to ", calc.path["input_geo"]) calc.write_geometry('init', des) print("write_geometry_files(): name ", el.name) stn.name = add_name_before + calc.name + '' + str( el.name) + '.' + str(calc.version) #stn = replic(stn, (1,2,2)) write_xyz(stn) print("__________________________\n\n\n") return def min_diff(f, list, diffprec): """ calculates difference between one number and list of other numbers. return the index of number with smallest difference. if difference is smaller than diffprec returns true as the second argument. """ #print list if list == []: return 0, False mind = min([abs(f - l) for l in list]) with_i = np.asarray([abs(f - l) for l in list]).argmin() return with_i, (mind < diffprec) 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], end=' ') #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], end=' ' ) # 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 """0. BEGIN-------------------------------------------------------------------------------""" hstring = ("%s #on %s" % (traceback.extract_stack(None, 2)[0][3], datetime.date.today())) if hstring != header.history[-1]: header.history.append(hstring) print_and_log("\n\n------Starting find_pairs()-----------...\n") if type(central_atoms) == int: #not in [tuple, list]: central_atoms = [central_atoms] #transform to list if type(in_calc) == str: in_calc_name = in_calc in_calc = CalculationVasp() #in_calc.name = str(in_calc_name) in_calc.name = base_name print("in_calc name is ", in_calc.name) in_calc.read_geometry(in_calc_name) if gbpos: in_calc.gbpos = gbpos #rewrite gbpos st = in_calc.init else: """End relaxed structure is used!!!""" st = copy.deepcopy(in_calc.end) in_calc_name = str(in_calc.id) """1. Create separate list of pores and remove them from xcart--------------------------------------------------------""" if "hcp_octa_xred": in_calc.init.name = segtyp + '_all_pores' rep = replic(in_calc.init, (2, 2, 2), -1) write_xyz(rep) #just to check """Coordinates of octapores provided in xcart; znucl = 200;""" xcart = st.xcart typat = st.typat st.typat = [] st.xcart = [] xcart_pores = [] #clean structure from pores with z == 200 and construct xcart_pores for i, x in enumerate(xcart): z = st.znucl[typat[i] - 1] if z == 200: xcart_pores.append(x) #print "Found pore" else: st.xcart.append(x) st.typat.append(typat[i]) st.natom = len(st.xcart) print('Number of found pores with znucl = 200 is ', len(xcart_pores)) for n in central_atoms: if n >= len(xcart_pores): raise RuntimeError """2. Preprocess segreg and grainvol cases--------------------------------------------------------""" # in_calc can be of two types: pure and with C in grain volume; using pure we construct co-segregation cases; using carbon in volume we can construct segregation cases if segtyp in ('segreg', 'grainvol'): if 2 in st.typat: # impurity in grain volume; (now assume that Carbon) iimp = st.typat.index(2) xcart1imp = st.xcart[iimp] #save coordinates of carbon atom del st.xcart[iimp] del st.typat[iimp] st.natom -= 1 #and remove it #del st.xred[iimp] st.ntypat -= 1 del st.znucl[1] print("Impurity atom was removed from cell") if xcart1imp: #for compatibility with previous cases; better not to use imp1 = len(xcart_pores) xcart_pores.append(xcart1imp) xcart2imp = xcart1imp - 0.5 * st.rprimd[ 0] #determine coordinates of second impurity assuming that we have mirror symmetry if xcart2imp[0] < 0: xcart2imp = xcart1imp + 0.5 * st.rprimd[0] imp2 = imp1 + 1 xcart_pores.append(xcart2imp) else: #new version; both central pores are found in the pure cell!!! #We have pure cell here; Find central pore in 1st grain and 2nd grain: xcen1 = in_calc.gbpos - 0.25 * st.rprimd[0][ 0] #x center of the first grain xcen2 = in_calc.gbpos - 0.75 * st.rprimd[0][ 0] #z center of the second grain # print "xcen", xcen1, xcen2 d1l = [] d2l = [] rpxx05 = st.rprimd[0][0] * 0.5 for x in xcart_pores: d1 = xcen1 - x[0] d2 = xcen2 - x[0] if d2 < -rpxx05: d2 += st.rprimd[0][ 0] # assuming that periodic boundary conditions needed only here d1l.append(abs(d1)) d2l.append(abs(d2)) # print d1,d2 imp1 = np.argmin(d1l) #needed numbers of pores imp2 = np.argmin(d2l) # print imp1, imp2 xcart1imp = xcart_pores[imp1] xcart2imp = xcart_pores[imp2] # print "xcartimp", xcart1imp, xcart2imp """3. Define central atoms for segregation and co-segregation cases--------------------------------------------------------""" max_dist_from_gb = 100 if segtyp in ('segreg', 'coseg'): # central_atoms = [] max_dist_between_atoms = 4.8 max_dist_from_gb = 3 #main controls for i, x in enumerate(xcart_pores): #generate central atoms d = abs(x[0] - in_calc.gbpos) if d < max_dist_from_gb: central_atoms.append(i) """4. Assume that we always have target_znucl, but only three !!!--------------------------------------------------------""" st.znucl = target_znucl #Please make this part more general st.ntypat = len(set(st.znucl)) print('Warning! Found only ', st.ntypat, 'of unique atoms in target_znucl') st.xred = xcart2xred(st.xcart, st.rprimd) """5. Find segreg and co-segreg cases--------------------------------------------------------""" in_calc.init = st dlist_coseg = [] if segtyp == 'coseg': print("\nStart searching pairs in gb") # main_path = 'T2/CO/' #! please make more general dlist_coseg = pairs(in_calc, xcart_pores, central_atoms, prec, max_dist_between_atoms, max_dist_from_gb, pairtyp='gb') dlist_coseg_exc = [] for el in copy.deepcopy( dlist_coseg): #Exchange C and O only for unsymmetrical cases if 's' in el[2]: continue # not needed for symmetrical cases el[2] = el[2].replace('C', 'x') el[2] = el[2].replace('O', 'C') el[2] = el[2].replace('x', 'O') el[7], el[8] = el[8], el[7] el[3], el[4] = el[4], el[3] dlist_coseg_exc.append(el) for el in dlist_coseg + dlist_coseg_exc: # Helper stname = base_name + el[2] path = main_path + base_name + '_coseg' print((( "struct_des['{0:s}'] = des('{1:s}', 'co-segregation configurations; made from " + based_on + "' )").format(stname, path))) for el in dlist_coseg + dlist_coseg_exc: # Helper stname = base_name + el[2] path = main_path + base_name + '_coseg' print( "add_loop('" + stname + "','" + based_on.split('.')[1] + "',range(1,5),calc,conv,varset, 'up1', inherit_option = 'inherit_xred')" ) write_geometry_files(dlist_coseg + dlist_coseg_exc, in_calc, xcart_pores, segtyp, take_final_rprimd_from, add_typat=add_typat) elif segtyp == 'segreg': """Produce segregation cases only in the case of segreg""" print("\nStart producing segragation cases") dlist_segreg = [] # dlist_segreg1 = copy.deepcopy(input_dlist_coseg) #in this case we use input_dlist with co-segragation cases from pure cell. # dlist_segreg2 = copy.deepcopy(input_dlist_coseg) #There is small error, because positions of pores at grain boundary # #differs in pure cell and cell with impurity in grain volume # for i, el in enumerate(input_dlist_coseg): # sym = '' # if 'is' in el[2]: sym = 'is' # elif 'ms' in el[2]: sym = 'ms' # dlist_segreg1[i][7] = xcart1imp # dlist_segreg1[i][2] = 'CvOi'+str(i+1)+sym # dlist_segreg2[i][8] = xcart1imp # dlist_segreg2[i][2] = 'CiOv'+str(i+1)+sym """new determination based on input_dlist_coseg[0][-1]""" el = copy.deepcopy(input_dlist_coseg[0]) unique = el[-1] #sums and coordinates of unique pores print("unique", unique) for i, sx in enumerate(unique): el[2] = 'Ci' + str(i + 1) + 'Ov' el[7] = sx[1] d1, dnext = image_distance(sx[1], xcart1imp, st.rprimd, 2) d2, dnext = image_distance(sx[1], xcart2imp, st.rprimd, 2) if d1 > d2: el[8] = xcart1imp else: el[8] = xcart2imp # the farthest impurity in grain volume is used dlist_segreg.append(copy.deepcopy(el)) #dlist_segreg = dlist_segreg1 + dlist_segreg2 #Segregation of the first impurity and of the second dlist_segreg_exc = [] for el in copy.deepcopy( dlist_segreg): #Exchange C and O only for unsymmetrical cases if 's' in el[2]: continue # not needed for symmetrical cases el[2] = el[2].replace('C', 'x') el[2] = el[2].replace('O', 'C') el[2] = el[2].replace('x', 'O') el[7], el[8] = el[8], el[7] el[3], el[4] = el[4], el[3] dlist_segreg_exc.append(el) #helper for el in dlist_segreg + dlist_segreg_exc: stname = base_name + el[2] path = main_path + base_name + '_segreg' print((( "struct_des['{0:s}'] = des('{1:s}', 'co-segregation configurations; made from " + based_on + "' )").format(stname, path))) for el in dlist_segreg + dlist_segreg_exc: stname = base_name + el[2] path = main_path + base_name + '_segreg' print( "add_loop('" + stname + "','" + based_on.split('.')[1] + "',range(1,5),calc,conv,varset, 'up1', inherit_option = 'inherit_xred')" ) write_geometry_files(dlist_segreg + dlist_segreg_exc, in_calc, xcart_pores, segtyp, take_final_rprimd_from, add_typat=add_typat) """6. Find volume cases--------------------------------------------------------""" # this part for construction volume cases if segtyp == "grainvol": #take care that you have only one carbon atom in the grain print("\nStart searching pairs in the volume") central_atoms = [imp1] max_dist_between_atoms = 4. #gvolume_config_num = 0 #please choose manually dlist = pairs(in_calc, xcart_pores, central_atoms, prec, max_dist_between_atoms, max_dist_from_gb, pairtyp='gvol') #dlist = [dlist[0], copy.deepcopy(dlist[gvolume_config_num-1]) ] #dlist[0][4] = imp2 #no matter wht was dlist[0]; used for vv case dlist.append(copy.deepcopy(dlist[0])) dlist[-1][8] = xcart2imp #last element for both atoms in grain volumes dlist[-1][2] = 'CvOvms' #helper for el in dlist: stname = base_name + el[2] path = main_path + base_name + '_gvol' #grain volume print( "add_loop('" + stname + "','" + based_on.split('.')[1] + "',range(1,5),calc,conv,varset, 'up1', inherit_option = 'inherit_xred')" ) for el in dlist: stname = base_name + el[2] path = main_path + base_name + '_gvol' #grain volume print((( "struct_des['{0:s}'] = des('{1:s}', 'co-segregation configurations; made from " + based_on + "' )").format(stname, path))) write_geometry_files(dlist, in_calc, xcart_pores, segtyp, take_final_rprimd_from, add_typat=add_typat) """. Triple cases--------------------------------------------------------""" def triples(addatom=('O', 3), dlist=[], tlist=[], in_calc=None, xcart_pores=[], max_dist_to_next_atom=3): """ Add addatom to all configurations in tlist; addatom[1] - type of atom in typat dlist - list of configurations with two impurity atoms; Used if tlist == []; the format of dlist is quiet special tlist - list of configurations with arbirtary number of atoms; RETURN: tlist - list of configurations with add atoms """ st = in_calc.init if dlist and tlist == []: for el in dlist: par = el print('pair 1', par, end=' ') x1 = par[7] x2 = par[8] print('x1 = ', x1) print('x2 = ', x2) config = Structure() config.xcart = [x1, x2] config.typat = [2, 3] config.name = el[2] tlist.append(config) tlist_new = [] for config in tlist: xcart = config.xcart typat = config.typat name = config.name print('\n\n\nStart to adding atom to ', name) i = 1 dlistlist = [] diffprec = 0.001 [dlistlist.append([]) for x in xcart] print(len(dlistlist)) for xpor in xcart_pores: skip = True for j, x in enumerate( xcart ): # list of 2 or 3 initial atoms to which additional atom will be added if all(np.around(xpor, 5) == np.around(x, 5)): skip = True break d1, d2 = image_distance( x, xpor, st.rprimd, 2) # the minimum distance and the next minimum dist if d1 > max_dist_to_next_atom: skip = True break #if only one pore is larger from atom than limit, the pore is skiped # suml = d11+d21+par[0] # for dl in dlistlist: # print 'j is ', j i_min, smaller = min_diff( d1, dlistlist[j], diffprec ) #old condition - bad - removes unique configurations #if smaller: skip = True; continue # symmetrical pores deleted dlistlist[j].append(d1) skip = False # all conditions are fullfilled - this configuration is unique # else: # print 'List of distances to atoms' if skip: continue # #print "Pore can be used", xpor #sum of distances for triple new = Structure() new.name = name + addatom[0] + str(i) new.xcart = copy.deepcopy(xcart) new.xcart.append(xpor) new.typat = copy.deepcopy(typat) new.typat.append(addatom[1]) # print 'new.typat =', new.typat #calculate sum of lengths new.length = 0 new.lengthCO = 0 new.lengthCC = 0 new.lengthOO = 0 new.xcartC = [] new.xcartO = [] for m, x1 in enumerate(new.xcart): if new.typat[m] == 2: new.xcartC.append(x1) if new.typat[m] == 3: new.xcartO.append(x1) for x2 in new.xcart: d1, d2 = image_distance(x1, x2, st.rprimd, 2) new.length += d1 for xC in new.xcartC: for xO in new.xcartO: d1, d2 = image_distance(xC, xO, st.rprimd, 2) new.lengthCO += d1 for xC1 in new.xcartC: for xC2 in new.xcartC: d1, d2 = image_distance(xC1, xC2, st.rprimd, 2) new.lengthCC += d1 for xO1 in new.xcartO: for xO2 in new.xcartO: d1, d2 = image_distance(xO1, xO2, st.rprimd, 2) new.lengthOO += d1 skip = False n = len(new.xcart) """additional conditions to leave only unique configurations""" for config in tlist_new: if 1: nr = 0 for (v1, t1) in zip(new.xcart, new.typat): for (v2, t2) in zip(config.xcart, config.typat): if all(np.around(v1, 8) == np.around( v2, 8)) and t1 == t2: nr += 1 if nr == n: print("The configurations", new.name, 'and', config.name, 'consist of the same atoms, continue') skip = True break # print all([ all( np.around(v1, 8) == np.around(v2, 8) ) for (v1, v2) in zip(new.xcart, config.xcart) ]) #check identity using sum of distances # i_min, smaller = min_diff(config.length, [new.length], diffprec) # if smaller: # print "Configuration ", new.name, "has the same sum of lengths as", config.name i_min, smaller1 = min_diff(config.lengthCO, [new.lengthCO], diffprec) i_min, smaller2 = min_diff(config.lengthCC, [new.lengthCC], diffprec) i_min, smaller3 = min_diff(config.lengthOO, [new.lengthOO], diffprec) # print 'Compare', new.name, config.name, smaller1, smaller2, smaller3 if smaller1 and smaller2 and smaller3: print( "\nConfiguration ", new.name, "has the same sum of C-O, C-C and O-O lengths as", config.name) print() skip = True break if skip: continue print('\nSum of CO lengths in :', new.name, new.lengthCC, new.lengthOO, new.lengthCO) tlist_new.append(new) i += 1 return tlist_new if segtyp == "bulk_triple" or segtyp == "bulk_pairs": # max_dist_between_atoms = 4.8 print("\nSearching pairs ...") dlist = pairs(in_calc, xcart_pores, central_atoms, prec, max_dist_between_atoms, pairtyp='gvol') if segtyp == "bulk_pairs": write_geometry_files(dlist, in_calc, xcart_pores, segtyp, take_final_rprimd_from, configver=True, add_typat=add_typat) if segtyp == "bulk_triple": max_dist_to_next_atom = 5.5 print("\nSearching triples ...") #, tlist tlist = [] tlist = triples(('O', 3), dlist, tlist, in_calc, xcart_pores, max_dist_to_next_atom) tlist = triples(('C', 2), dlist, tlist, in_calc, xcart_pores, max_dist_to_next_atom) write_geometry_files(dlist, in_calc, xcart_pores, segtyp, take_final_rprimd_from, tlist=tlist, configver=True, add_typat=add_typat) return dlist_coseg, xcart_pores
def insert_cluster(insertion, i_center, matrix, m_center): """ Take care of orientation; typat should be consistent Input: insertion - object of class Structure(), which is supposed to be inserted in matrix in such a way that i_center will be combined with m_center. matrix - object of class Structure(). i_center, m_center - numpy arrays (3). """ ins = copy.deepcopy(insertion) mat = copy.deepcopy(matrix) r = mat.rprimd for i, z in enumerate(ins.znucl): if z not in mat.znucl: mat.znucl.append(z) mat.ntypat += 1 mat.nznucl.append(ins.nznucl[i]) hproj = [(r[0][i] + r[1][i] + r[2][i]) * 0.5 for i in (0, 1, 2)] #projection of vectors on three axis for i, x in enumerate(ins.xcart): ins.xcart[i] = x - i_center for i, x in enumerate(mat.xcart): mat.xcart[i] = x - m_center max_dis = 1 for i_x, ix in enumerate(ins.xcart): dv_min = max_dis print_and_log( "Insertion atom ", ix, ) for j, mx in enumerate(mat.xcart): dv = mx - ix for i in 0, 1, 2: if dv[i] > hproj[i]: dv = dv - mat.rprimd[ i] #periodic boundary conditions - can be not correct (in the sense that closest image can lie not 100 % in the neighbourhood image cell ) for oblique cells and large absolute values of dv if dv[i] < -hproj[i]: dv = dv + mat.rprimd[i] len1 = np.linalg.norm(dv) len2, second_len2 = mat.image_distance(mx, ix, r, 2) #check len1 #print "Lengths calculated with two methods ", len1, len2 len1 = len2 #just use second method #assert np.around(len1,1) == np.around(len2,1) if len1 < dv_min: dv_min = len1 j_r = j # number of matrix atom to replace if dv_min == max_dis: print_and_log(" is more far away from any matrix atom than ", dv_min, " A; I insert it") mat.xcart.append(ix) print_and_log('type of added atom is ', ins.typat[i_x]) mat.typat.append(ins.typat[i_x]) else: print_and_log("will replace martix atom", mat.xcart[j_r]) mat.xcart[j_r] = ix.copy() mat.rprimd = r mat.xcart2xred() mat.natom = len(mat.xcart) mat.name = 'test_of_insert' write_xyz(mat) return mat