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)
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()]
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)
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)
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")
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")
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
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
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"] = (
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