Esempio n. 1
0
def complex_nn(start,
               elements,
               cut_off_dict,
               end=None,
               ox_states=None,
               txt_fname='nn_data.txt',
               return_df=False):
    """
    Finds the nearest neighbours for more complex structures. Uses CutOffDictNN()
    class as the nearest neighbour method. Check validity on bulk structure
    before applying to surface slabs.
    Args:
        start (str): filename of structure to analyse
        elements (list): the elements in the structure, order does not matter
        cut_off_dict (dict): dictionary of bond lengths
        i.e. {('Ag','S'): 3.09, ('La','O'): 2.91, ('La','S'): 3.36,
              ('Ti','O'): 2.35, ('Ti','S'): 2.75, ('Cu','S'): 2.76}
        end (str): filename of structure to analyse, use if comparing initial
        and final structures, the compared structures must have same constituent
        atoms and number of sites; default=None
        ox_states (list or dict): add oxidation states either by sites
        i.e. [3, 2, 2, 1, -2, -2, -2, -2] or by element i.e. {'Fe': 3, 'O':-2}.
        If the structure is decorated with oxidation states, the bond distances
        need to have oxidation states specified. Default=None (no oxidation states)
        txt_fname (str): filename of csv file, default='nn_data.txt'
        return_df (bool): returns the DataFrame; default=False
    Returns
        txt file with atoms and the number of nearest neighbours
    """

    # Instantiate start structure object
    start_struc = Structure.from_file(start)

    # Add atom site labels to the structure
    el_dict = {i: 1 for i in elements}
    site_labels = []
    for site in start_struc:
        symbol = site.specie.symbol
        site_labels.append((symbol, el_dict[symbol]))
        el_dict[symbol] += 1
    start_struc.add_site_property('', site_labels)

    # Adds oxidation states by guess by default or if the provided oxidation
    # states are antyhing but a list or a dict
    if type(ox_states) is dict:
        start_struc.add_oxidation_state_by_element(ox_states)
    elif type(ox_states) is list:
        start_struc.add_oxidation_state_by_site(ox_states)
    else:
        start_struc.add_oxidation_state_by_guess(max_sites=-1)

    # Instantiate the nearest neighbour algorithm
    codnn = CutOffDictNN(cut_off_dict=cut_off_dict)

    # Get the bonded end structure
    bonded_start = codnn.get_bonded_structure(start_struc)

    # Nearest neighbours for just one structure
    if not end:
        nn_list = []
        for n, site in enumerate(start_struc):
            cn_start = bonded_start.get_coordination_of_site(n)
            label = site_labels[n]
            nn_list.append({
                'site': n + 1,
                'atom': label,
                'cn start': cn_start
            })

        # Make a dataframe from nn_list and return if needed
        df = pd.DataFrame(nn_list)
        df.to_csv(txt_fname, header=True, index=False, sep='\t', mode='w')

        if return_df:
            return df

    #nearest neighbour for two compared structures
    else:
        end_struc = Structure.from_file(end)

        # Adds oxidation states by guess by default or if the provided oxidation
        # states are antyhing but a list or a dict
        if type(ox_states) is dict:
            end_struc.add_oxidation_state_by_element(ox_states)
        elif type(ox_states) is list:
            end_struc.add_oxidation_state_by_site(ox_states)
        else:
            end_struc.add_oxidation_state_by_guess(max_sites=-1)

        # Get the bonded end structure
        bonded_end = codnn.get_bonded_structure(end_struc)

        # Get coordination numbers for start and end structures, calculates the
        # end - start difference, collects the site labels and passes it all to
        # a dataframe
        nn_list = []
        for n, site in enumerate(start_struc):
            cn_start = bonded_start.get_coordination_of_site(n)
            cn_end = bonded_end.get_coordination_of_site(n)
            cn_diff = cn_end - cn_start
            label = site_labels[n]
            nn_list.append({
                'site': n + 1,
                'atom': label,
                'cn start': cn_start,
                'cn_end': cn_end,
                'diff': cn_diff
            })

        # Make a dataframe from nn_list and return if needed
        df = pd.DataFrame(nn_list)
        df.to_csv(txt_fname, header=True, index=False, sep='\t', mode='w')

        if return_df:
            return df
Esempio n. 2
0
def complex_nn(start,
               cut_off_dict,
               end=None,
               ox_states=None,
               save_csv=True,
               csv_fname='nn_data.csv'):
    """
    Finds the nearest neighbours for more complex structures. Uses CutOffDictNN()
    class as the nearest neighbour method. Check validity on bulk structure
    before applying to surface slabs. 

    The ``site_index`` in the produced DataFrame or csv file is one-indexed and 
    represents the atom index in the structure. 

    Args:
        start (`str`): filename of structure, takes all pymatgen-supported formats.
        cut_off_dict (`dict`): Dictionary of bond lengths. The bonds should be 
            specified with the oxidation states\n
            e.g. ``{('Bi3+', 'O2-'): 2.46, ('V5+', 'O2-'): 1.73}``
        end (`str`, optional): filename of structure to analyse, use if 
            comparing initial and final structures. The structures must have 
            same constituent atoms and number of sites. Defaults to ``None``. 
        ox_states (``None``, `list` or  `dict`, optional): Add oxidation states 
            to the structure. Different types of oxidation states specified will 
            result in different pymatgen functions used. The options are: 
            
            * if supplied as ``list``: The oxidation states are added by site 
                    
                    e.g. ``[3, 2, 2, 1, -2, -2, -2, -2]``
            
            * if supplied as ``dict``: The oxidation states are added by element
                    
                    e.g. ``{'Fe': 3, 'O':-2}``
            
            * if ``None``:  The oxidation states are added by guess.  

            Defaults to ``None`` 
        save_csv (`bool`, optional): Save to a csv file. Defaults to ``True``.
        csv_fname (`str`, optional): Filename of the csv file. Defaults to 
            ``'nn_data.csv'``
    
    Returns
        None (default) or DataFrame containing coordination data.
    """
    # Instantiate start structure object
    start_struc = Structure.from_file(start)

    # Add atom site labels to the structure
    els = ''.join([i for i in start_struc.formula
                   if not i.isdigit()]).split(' ')
    el_dict = {i: 1 for i in els}
    site_labels = []
    for site in start_struc:
        symbol = site.specie.symbol
        site_labels.append((symbol, el_dict[symbol]))
        el_dict[symbol] += 1
    start_struc.add_site_property('', site_labels)

    # Add oxidation states
    start_struc = oxidation_states(start_struc, ox_states=ox_states)

    # Instantiate the nearest neighbour algorithm and get bonded structure
    codnn = CutOffDictNN(cut_off_dict=cut_off_dict)
    bonded_start = codnn.get_bonded_structure(start_struc)

    # Instantiate the end structure if provided
    if end:
        end_struc = Structure.from_file(end)
        end_struc = oxidation_states(end_struc, ox_states=ox_states)
        bonded_end = codnn.get_bonded_structure(end_struc)

    # Iterate through structure, evaluate the coordination number and the
    # nearest neighbours specie for start and end structures, collects the
    # symbol and index of the site (atom) evaluated and its nearest neighbours
    df_list = []
    for n, site in enumerate(start_struc):
        cn_start = bonded_start.get_coordination_of_site(n)
        coord_start = bonded_start.get_connected_sites(n)
        specie_list = []
        for d in coord_start:
            spc = d.site.specie.symbol
            specie_list.append(spc)
        specie_list.sort()
        site_nn_start = ' '.join(specie_list)
        label = site_labels[n]

        if end:
            cn_end = bonded_end.get_coordination_of_site(n)
            coord_end = bonded_end.get_connected_sites(n)
            specie_list = []
            for d in coord_end:
                spc = d.site.specie.symbol
                specie_list.append(spc)
            specie_list.sort()
            site_nn_end = ' '.join(specie_list)
            df_list.append({
                'site': n + 1,
                'atom': label,
                'cn start': cn_start,
                'nn_start': site_nn_start,
                'cn_end': cn_end,
                'nn_end': site_nn_end
            })

        else:
            df_list.append({
                'site_index': n + 1,
                'site': label,
                'cn_start': cn_start,
                'nn_start': site_nn_start
            })

    # Make a dataframe from df_list
    df = pd.DataFrame(df_list)

    # Save the csv file or return as dataframe
    if save_csv:
        if not csv_fname.endswith('.csv'):
            csv_fname += '.csv'
        df.to_csv(csv_fname, header=True, index=False)
    else:
        return df