示例#1
0
文件: calcul.py 项目: Kurufinve/siman
def site_repulsive_e(st, i):
    """
    calculate repulsive energy for site i of structure st
    """

    xc = st.xcart[i]
    el = st.get_elements()
    E = 0
    for j, x in enumerate(st.xcart):
        d = image_distance(xc, x, st.rprimd)[0]
        E += buck(d, el[i], el[j])

    return E
示例#2
0
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
示例#3
0
def make_defect(cl,
                el,
                st_type='end',
                option='vac',
                pos=None,
                ise=None,
                opt_vol=0,
                suf='',
                it_folder=None,
                el_rep='',
                pos_rep=1,
                pos_rep2=None,
                polaron_pos=None,
                occ_matrix=None,
                up=0,
                fit=0,
                outcar=None,
                only_read=0,
                Eref=0,
                compat1=False,
                add_loop_arg={}):
    """
    Function allow to create point defects and run them
	previous name: make_vacancy()


    cl - starting Calculation 
    st_type - starting structure of cl: 'init' or 'end' 
    el - element to be removed or replaced
    
    option -
        'vac'  - make vacancy
        'rep'  - replace one atom with 'el_rep', 
        'pair' - make vacancy -Ti complex for V-Ti project 

    pos - unique position of el if non-eqivalent atoms exist - for vac
    pos_rep - number of position to replace from 0

    ise - new set
    opt_vol (bool) - optimize volume

    suf (str) - mannually added suffix
    it_folder - mannually provided it_folder

    up (bool) - [ 0, 1 ] update current calculation
    fit = 0,  outcar = None, only_read = 0 - flow control as usual

    polaron_pos - choose polaron position
    occ_matrix - list of lists see format in classes


    compat1 - compatability with previous calculations, which were used for Na2FePO4F project


    Eref - reference energy for solution energy

    TODO: rename to ?_point_defects()
    """

    from siman.project_funcs import e_bind

    if pos == None:
        pos = ''

    if polaron_pos == None:
        pol_suf = ''
    else:
        pol_suf = '.p' + str(polaron_pos)  # polaron suffix

    ssuf = el + str(pos) + el_rep + pol_suf + suf
    if 'su' in cl.id[0] and not 'su.' in cl.id[0]:
        it_new = cl.id[0].replace('su', option) + ssuf
    else:
        it_new = cl.id[0] + option + ssuf

    if compat1:  # no element in name
        it_new = cl.id[0].replace('su', 'vac') + str(pos)

    id_new, st, it_folder = prepare(it_new, opt_vol, it_folder, ise, cl,
                                    st_type, option)
    occfile = None
    if not only_read and (up or id_new not in calc):
        # it_new

        if 'vac' in option:
            st_del1, i_del = remove_one_atom(st, el, pos)
            st_vis = st.replace_atoms([i_del], 'U')
            st_vis.name = it_new + '_visual'
            st_vis.write_xyz()

            #possible polaron positions
            tr = st.get_transition_elements(fmt='z')
            i_tr = st.get_transition_elements(fmt='n')
            # dist = []
            max_d = 0
            i_max_d = None
            for i in i_tr:
                d1, d2 = image_distance(st.xcart[i], st.xcart[i_del],
                                        st.rprimd)
                # print(i+1, d1, d2)
                if d1 < d2:
                    if d1 > max_d:
                        max_d = d1
                        i_max_d = i
            if i_max_d is not None:
                print(
                    'The longest distance to transition metal in current supercell is ',
                    max_d, 'A for atom', i_max_d + 1,
                    st.get_elements()[i_max_d])

            numb = st.nn(i_del, from_one=0, n=len(tr) + 5,
                         only=list(set(tr)))['numbers'][1:]

            printlog(
                'Choose polaron position starting from 1 using *polaron_pos*',
                imp='y')
            if polaron_pos:
                i_pol = numb[polaron_pos - 1]
                printlog('atom',
                         i_pol + 1,
                         st.get_elements()[i_pol],
                         'is chosen',
                         imp='y')
                # print(numb)
                # sys.exit()
                #take_occupation matrices from cl
                print('substitution occupation matrix of atom', i_pol + 1)
                occ_matrices = copy.deepcopy(cl.occ_matrices)
                occ_matrices[i_pol] = occ_matrix
                # print(pd.DataFrame(cl.occ_matrices[i_pol]))
                occfile = write_occmatrix(occ_matrices, cl.dir + '/occ/')
                # print(occfile)

                # sys.exit()
            else:
                occfile = None

        elif 'rep' in option:
            st_del1 = st.replace_atoms([pos_rep], el_rep)
            print(
                'Atom',
                str(pos_rep),
                st.get_elements()[pos_rep],
                ' replaced with',
                el_rep,
            )
            print(st_del1.get_elements()[pos_rep])
            st_del1.name = it_new

        elif 'pair' in option:
            st_del1 = st.replace_atoms([1], el_rep)
            if 'pair2' in option:
                st_del1 = st_del1.replace_atoms([pos_rep2], el_rep)

            st_del1 = remove_one_atom(st_del1, el, pos)
            print('Atom 1 replaced with', el, 'and atom removed')
            st_del1.name = it_new

        st_del1.write_xyz()

        if opt_vol:
            it = add_loop(it_new,
                          ise,
                          1,
                          calc_method='uniform_scale',
                          scale_region=(-4, 4),
                          inherit_option='inherit_xred',
                          input_st=st_del1,
                          it_folder=it_folder,
                          params={'occmatrix': occfile},
                          **add_loop_arg)
        else:
            it = add_loop(it_new,
                          ise,
                          1,
                          input_st=st_del1,
                          it_folder=it_folder,
                          params={'occmatrix': occfile},
                          **add_loop_arg)

    else:
        if opt_vol and fit:
            res_loop(it_new + '.su',
                     ise,
                     list(range(1, 8)) + [100],
                     analys_type='fit_a',
                     show='fitfo',
                     up='2',
                     choose_outcar=outcar)
        else:
            res_loop(*id_new, up='2', choose_outcar=outcar, show='fo')
            # calc[it_new+'.su', ise, 100].end.jmol()

        cl_v = calc[id_new]
        if '4' not in cl.state:
            cl.res()
        if not hasattr(cl_v, 'e0'):
            printlog('Warning', cl_v.id, 'is bad')
            return
        calc_redox(cl_v, cl)
        # print(cl_v.end.vol, cl.end.vol)
        dE = None

        if option == 'vac':
            cl_v.res()
            cl.res()
            print('Evac = {:3.2f} eV'.format(cl_v.e0 - cl.e0 / cl.end.natom *
                                             cl_v.end.natom))

        elif option == 'rep':
            diffE, diffV = matrix_diff(cl_v, cl)

            print('Esol = {:3.2f} eV'.format(diffE - Eref))

        elif 'pair' in option:
            ''
            cl_bulk = cl
            cl_pair = cl_v
            it = cl.id[0]
            if 'V54' in it:
                it = it.replace('.su', '.')
            id_vac = (it + 'vacV', cl.id[1], 1)
            id_sol = (it + 'repTi', cl.id[1], 1)
            cl_vac = calc[id_vac]
            cl_sol = calc[id_sol]

            # print(id_vac, id_sol)

            # if  option == 'pair':
            dE = e_bind(cl_bulk, cl_vac, cl_sol, cl_pair)
            print('Ecomplex = {:3.2f} eV'.format(dE))

            # elif '2' in option:

        return {'dE': dE, 'N': cl_v.end.natom, 'Name': cl.id}
示例#4
0
    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
示例#5
0
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
示例#6
0
    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