Beispiel #1
0
    def test_cn(self):

        nn = CutOffDictNN({('C', 'C'): 2})
        self.assertEqual(nn.get_cn(self.diamond, 0), 4)

        nn_null = CutOffDictNN()
        self.assertEqual(nn_null.get_cn(self.diamond, 0), 0)
Beispiel #2
0
    def get_bonds(self):
        graph = StructureGraph.with_local_env_strategy(
            self.structure, CutOffDictNN({("Si", "O"): 2.0, ("O", "Si"): 2.0})
        )

        # GULP starts counting on 1
        return [(u + 1, v + 1, "single") for u, v in graph.graph.edges()]
Beispiel #3
0
    def test_cn(self):

        nn = CutOffDictNN({('C', 'C'): 2})
        self.assertEqual(nn.get_cn(self.diamond, 0), 4)

        nn_null = CutOffDictNN()
        self.assertEqual(nn_null.get_cn(self.diamond, 0), 0)
Beispiel #4
0
    def test_properties(self):

        self.assertEqual(self.mos2_sg.name, "bonds")
        self.assertEqual(self.mos2_sg.edge_weight_name, "bond_length")
        self.assertEqual(self.mos2_sg.edge_weight_unit, "Å")
        self.assertEqual(self.mos2_sg.get_coordination_of_site(0), 6)
        self.assertEqual(len(self.mos2_sg.get_connected_sites(0)), 6)
        self.assertTrue(
            isinstance(self.mos2_sg.get_connected_sites(0)[0].site, PeriodicSite)
        )
        self.assertEqual(str(self.mos2_sg.get_connected_sites(0)[0].site.specie), "S")
        self.assertAlmostEqual(
            self.mos2_sg.get_connected_sites(0, jimage=(0, 0, 100))[0].site.frac_coords[
                2
            ],
            100.303027,
        )

        # these two graphs should be equivalent
        for n in range(len(self.bc_square_sg)):
            self.assertEqual(
                self.bc_square_sg.get_coordination_of_site(n),
                self.bc_square_sg_r.get_coordination_of_site(n),
            )

        # test we're not getting duplicate connected sites
        # thanks to Jack D. Sundberg for reporting this bug

        # known example where this bug occurred due to edge weights not being
        # bit-for-bit identical in otherwise identical edges
        nacl_lattice = Lattice(
            [
                [3.48543625, 0.0, 2.01231756],
                [1.16181208, 3.28610081, 2.01231756],
                [0.0, 0.0, 4.02463512],
            ]
        )
        nacl = Structure(nacl_lattice, ["Na", "Cl"], [[0, 0, 0], [0.5, 0.5, 0.5]])

        nacl_graph = StructureGraph.with_local_env_strategy(
            nacl, CutOffDictNN({("Cl", "Cl"): 5.0})
        )

        self.assertEqual(len(nacl_graph.get_connected_sites(1)), 12)
        self.assertEqual(len(nacl_graph.graph.get_edge_data(1, 1)), 12)
Beispiel #5
0
    def test_from_preset(self):
        nn = CutOffDictNN.from_preset("vesta_2019")
        self.assertEqual(nn.get_cn(self.diamond, 0), 4)

        # test error thrown on unknown preset
        self.assertRaises(ValueError, CutOffDictNN.from_preset, "test")
Beispiel #6
0
    def test_from_preset(self):
        nn = CutOffDictNN.from_preset("vesta_2019")
        self.assertEqual(nn.get_cn(self.diamond, 0), 4)

        # test error thrown on unknown preset
        self.assertRaises(ValueError, CutOffDictNN.from_preset, "test")
Beispiel #7
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
Beispiel #8
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
Beispiel #9
0
    VoronoiNN,
)
from pymatgen.core import Molecule, Structure

THIS_DIR = os.path.dirname(os.path.realpath(__file__))

with open(os.path.join(THIS_DIR, "atom_typing_radii.yml"), "r") as handle:
    ATOM_TYPING_CUTOFFS = yaml.load(handle, Loader=yaml.UnsafeLoader)

with open(os.path.join(THIS_DIR, "li_radii.yml"), "r") as handle:
    LI_TYPING_CUTOFFS = yaml.load(handle, Loader=yaml.UnsafeLoader)

with open(os.path.join(THIS_DIR, "tuned_vesta.yml"), "r") as handle:
    VESTA_CUTOFFS = yaml.load(handle, Loader=yaml.UnsafeLoader)

VESTA_NN = CutOffDictNN(cut_off_dict=VESTA_CUTOFFS)
ATR_NN = CutOffDictNN(cut_off_dict=ATOM_TYPING_CUTOFFS)
LI_NN = CutOffDictNN(cut_off_dict=LI_TYPING_CUTOFFS)


def construct_clean_graph(structure: Structure,
                          structure_graph: StructureGraph) -> nx.Graph:
    """Creates a networkx graph with atom numbers as node labels"""
    edges = {(u, v)
             for u, v, d in structure_graph.graph.edges(keys=False, data=True)}
    graph = nx.Graph()
    graph.add_edges_from(edges)
    for node in graph.nodes:

        graph.nodes[node]["specie"] = str(structure[node].specie)
        graph.nodes[node]["specie-cn"] = (
Beispiel #10
0
def get_NNs_pm(atoms, site_idx, NN_method):
    """
	Get coordinating atoms to the adsorption site

	Args:
		atoms (Atoms object): atoms object of MOF

		site_idx (int): ASE index of adsorption site
		
		NN_method (string): string representing the desired Pymatgen
		nearest neighbor algorithm:
		refer to http://pymatgen.org/_modules/pymatgen/analysis/local_env.html
	
	Returns:
		neighbors_idx (list of ints): ASE indices of coordinating atoms
	"""
    #Convert ASE Atoms object to Pymatgen Structure object
    bridge = pm_ase.AseAtomsAdaptor()
    struct = bridge.get_structure(atoms)

    #Select Pymatgen NN algorithm
    NN_method = NN_method.lower()
    if NN_method == 'vire':
        nn_object = MinimumVIRENN()
    elif NN_method == 'voronoi':
        nn_object = VoronoiNN()
    elif NN_method == 'jmol':
        nn_object = JmolNN()
    elif NN_method == 'min_dist':
        nn_object = MinimumDistanceNN()
    elif NN_method == 'okeeffe':
        nn_object = MinimumOKeeffeNN()
    elif NN_method == 'brunner_real':
        nn_object = BrunnerNN_real()
    elif NN_method == 'brunner_recpirocal':
        nn_object = BrunnerNN_reciprocal()
    elif NN_method == 'brunner_relative':
        nn_object = BrunnerNN_relative()
    elif NN_method == 'econ':
        nn_object = EconNN()
    elif NN_method == 'dict':
        #requires a cutoff dictionary located in the pwd
        nn_object = CutOffDictNN(cut_off_dict='cut_off_dict.txt')
    elif NN_method == 'critic2':
        nn_object = Critic2NN()
    elif NN_method == 'openbabel':
        nn_object = OpenBabelNN()
    elif NN_method == 'covalent':
        nn_object = CovalentBondNN()
    elif NN_method == 'crystal':
        nn_object = CrystalNN(porous_adjustment=True)
    elif NN_method == 'crystal_nonporous':
        nn_object = CrystalNN(porous_adjustment=False)
    else:
        raise ValueError('Invalid NN algorithm specified')

    #Find coordinating atoms
    with warnings.catch_warnings():
        warnings.simplefilter('ignore')
        neighbors = nn_object.get_nn_info(struct, site_idx)
    neighbors_idx = []
    for neighbor in neighbors:
        neighbors_idx.append(neighbor['site_index'])

    return neighbors_idx