def add_cap_ox(clust): # TODO: fix bug where adds multiple oxygen's to the same place """ Args: clust: """ nl = NeighborList(natural_cutoffs(clust), bothways=True, self_interaction=False) nl.update(clust) new_clust = clust cap_inds = get_cap_si(clust) for ind in cap_inds: while len(nl.get_neighbors(ind)[0]) < 4: neighb = nl.get_neighbors(ind)[0][ -1] # last index in the list of neighbor indicies direction = clust.get_positions()[ind] - clust.get_positions()[ neighb] # vector pointing from neighbor to Si ox_pos = clust.get_positions( )[ind] + 1.6 * direction / np.linalg.norm(direction) new_ox = Atom('O', position=ox_pos) new_clust.append(new_ox) nl = NeighborList(natural_cutoffs(clust), bothways=True, self_interaction=False) nl.update(clust) return (new_clust)
def test_cutoffs(): from ase.neighborlist import natural_cutoffs from ase import Atoms import numpy as np atoms = Atoms("HCOPtAu") assert np.allclose(natural_cutoffs(atoms), [0.31, 0.76, 0.66, 1.36, 1.36]) assert np.allclose(natural_cutoffs(atoms, mult=1.2), [0.372, 0.912, 0.792, 1.632, 1.632]) assert np.allclose(natural_cutoffs(atoms, mult=1.2, Au=1), [0.372, 0.912, 0.792, 1.632, 1])
def get_Z_TM(self, atoms, d_Z_TM, TM_type): # todo: improve flexibility to allow Z-TM-H or Z-TM-OH insertions while avoid overlapping nl = NeighborList(natural_cutoffs(atoms), bothways=True, self_interaction=False) nl.update(atoms) index_Al = [a.index for a in atoms if a.symbol == 'Al'] indices, offsets = nl.get_neighbors(index_Al[0]) indices = [val for val in indices if atoms[val].symbol == 'O'] assert len(indices) == 4 dict_Z_TM = {} original_atoms = copy.copy(atoms) pairs = list(itertools.combinations(indices, 2)) for i, pair in enumerate(pairs): atoms = copy.copy(original_atoms) v = self._get_direction_of_insertion(atoms, index_Al[0], pair[0], pair[1], tag='TM') atoms = atoms + Atoms( TM_type, positions=[atoms[index_Al[0]].position] + v * d_Z_TM) atoms.wrap() key_tag = 'O' + str(pair[0]) + '_O' + str(pair[1]) dict_Z_TM[key_tag] = atoms return dict_Z_TM
def get_all_Z_TM(self, d_Z_TM, TM_type): """ :param d_Z_TM: Z-TM distance with Z being the T sites on the zeolite framework and TM being extraframework atom to be inserted :return: a dictionary of structures for each T site name """ dict_Z_TM = {} for site_name, all_zeo_with_same_T in self.dict_1Al_replaced.items(): atoms = copy.copy(all_zeo_with_same_T[0]) nl = NeighborList(natural_cutoffs(atoms), bothways=True, self_interaction=False) nl.update(atoms) index_Al = [a.index for a in atoms if a.symbol == 'Al'] indices, offsets = nl.get_neighbors(index_Al[0]) assert len(indices) == 4 traj = [] pairs = list(itertools.combinations(indices, 2)) for i, pair in enumerate(pairs): atoms = copy.copy(all_zeo_with_same_T[0]) v = self._get_direction_of_insertion(atoms, index_Al[0], pair[0], pair[1], tag='TM') atoms = atoms + Atoms( TM_type, positions=[atoms[index_Al[0]].position] + v * d_Z_TM) traj.append(atoms) dict_Z_TM[site_name] = traj return dict_Z_TM
def get_angles(cluster, mult=1, excluded_index=None, excluded_pair=None): """ #TODO: consider combining get_bonds and get_angles function ase.geometry.analysis.Analysis.unique_angles function does not work, return all angles. three-body interactions. :param excluded_pair: excluding all [particle1, particle2, particle3] lists involving the excluded pair """ if excluded_index is None: excluded_index = [] if excluded_pair is None: excluded_pair = [] nl = NeighborList(natural_cutoffs(cluster, mult=mult), bothways=True, self_interaction=False) nl.update(cluster) angle_list, shortened_list = [], [] for count, indices in enumerate(Analysis(cluster, nl=nl).all_angles[0]): for index in indices: if all( list(val) not in angle_list for val in list( permutations([count, index[0], index[1]]))): angle_list.append([count, index[0], index[1]]) for angle in angle_list: if all(single_index not in angle for single_index in excluded_index) and \ all(list(value) not in excluded_pair for value in list(permutations(angle, 2))): shortened_list.append(angle) return angle_list, shortened_list
def get_bonds(cluster, mult=1, excluded_index=None, excluded_pair=None): """ Using ase.geometry.analysis.Analysis to get all bonds, then remove the repeated ones. Function also allows removing certain bonding pair defined by user (excluded_pair). Or removing pairs including certain atomic indices (excluded_index). :param cluster: :param mult: :param excluded_index: list of integers :param excluded_pair: list of lists :return: full bonding list, shortened list. If both excluded_index and excluded_pair are None, bonding list == shortened list """ if excluded_index is None: excluded_index = [] if excluded_pair is None: excluded_pair = [] nl = NeighborList(natural_cutoffs(cluster, mult=mult), bothways=True, self_interaction=False) nl.update(cluster) bond_list, shortened_list = [], [] for count, indices in enumerate(Analysis(cluster, nl=nl).all_bonds[0]): for index in indices: if [count, index] not in bond_list and [index, count ] not in bond_list: bond_list.append([count, index]) for bond in bond_list: if all(single_index not in bond for single_index in excluded_index) and \ all(tuple(bond) not in list(permutations(pair)) for pair in excluded_pair): shortened_list.append(bond) return bond_list, shortened_list
def atoms2graph(atoms, kwargs={}): unit_cell = atoms.get_cell() cutoffs = neighborlist.natural_cutoffs(atoms) NL = neighborlist.NewPrimitiveNeighborList( cutoffs, self_interaction=False, skin=0.1) # default atom cutoffs work well NL.build([False, False, False], unit_cell, atoms.positions) G = nx.Graph() for a, ccoord in zip(atoms, atoms.positions): ind = a.index + 1 G.add_node(a.symbol + str(ind), element_symbol=a.symbol, ccoord=ccoord, fcoord=np.array([0.0, 0.0, 0.0]), freeze_code='0', **kwargs) for a in G.nodes(): nbors = [ atoms[i].symbol + str(atoms[i].index + 1) for i in NL.get_neighbors(int(nl(a)) - 1)[0] ] for nbor in nbors: G.add_edge(a, nbor, bond_type='', bond_sym='.', bond_length=0) return G
def find_neighbor_index(cell, defectIndex, neighborOffset): # very similar to generating the mask indices cutOff = natural_cutoffs(cell, 1) nl = neighborlist.NeighborList(cutOff, 0.3, False, False, False) nl.update(cell) index_nei, offset_nei = nl.get_neighbors(defectIndex) # if index_nei.any(): # return index_nei[0 + neighborOffset] # else: # return defectIndex + 1 try: return index_nei[0 + neighborOffset] except IndexError: print('Warning: The defect atom does not have enough neighbor for neighborOffset = ', neighborOffset) print('Warning: Falling back to first neighbor') try: return index_nei[0] except: print('Warning: The defect atom does not have any available neighbor') print('Warning: Falling further back to next atom') return defectIndex + 1
def read_cif(cif): atoms = read(cif, format='cif') atoms.set_pbc(True) cutoffs = neighborlist.natural_cutoffs(atoms) unit_cell = atoms.get_cell() neighborlist.primitive_neighbor_list NL = neighborlist.NewPrimitiveNeighborList(cutoffs, use_scaled_positions=True, self_interaction=False, skin=0.1) NL.build([True, True, True], unit_cell, atoms.get_scaled_positions()) G = nx.Graph() for a, fcoord in zip(atoms, atoms.get_scaled_positions()): G.add_node(a.symbol + str(a.index), element_symbol=a.symbol, fcoord=fcoord) for a in G.nodes(): nbors = [ atoms[i].symbol + str(atoms[i].index) for i in NL.get_neighbors(int(nl(a)))[0] ] for nbor in nbors: G.add_edge(a, nbor) return G, np.asarray(unit_cell).T
def nest(atoms, index): position = atoms[index].position # position of that t site # This centers the atom object on the T site we want to remove center = atoms.get_center_of_mass() trans = center - position atoms.translate(trans) atoms.wrap() # get the neighbor list cutoff = neighborlist.natural_cutoffs(atoms, mult=1.05) nl = neighborlist.NeighborList(cutoffs=cutoff, self_interaction=False, bothways=True) nl.update(atoms) oxygens = nl.get_neighbors(index)[0] # add a hydrogen next to each neighbor oxygen for o in oxygens: vector = position - atoms[o].position hyd = molecule('H') new_location = atoms[o].position + vector / (vector**2).sum()**.5 hyd.translate(new_location) atoms = atoms + hyd # recenter the atoms back to their original position and delete the Si atoms.translate(-trans) atoms.wrap() del (atoms[index]) return atoms
def get_cluster_indices_multi_T_site(zeolite, T_indices: Iterable[int], max_size: int, max_neighbors: int) -> List[int]: """ get the indices of a cluster from a zeolite when specifying the center atom index and size of the cluster :param T_indices: Indices of the T site :type T_indices: Iterable[int] :param zeolite: the zeolite from which to build the cluster :param index: the centeral atom index :param max_size: the max number of atoms in the final cluster :param max_neighbors: the max number of neighbors from the starting cluster :return: a list of indices """ nl = NeighborList(natural_cutoffs(zeolite), self_interaction=False, bothways=True) nl.update(zeolite) cluster_indices = set() new_cluster_indices = set(T_indices) for _ in range(max_neighbors): current_cluster_indices = set() for cluster_index in new_cluster_indices: cluster_indices.add(cluster_index) if len(cluster_indices) >= max_size: return list(cluster_indices) for new_index in nl.get_neighbors(cluster_index)[0]: if new_index not in T_indices: # don't add T sites to current cluster indices current_cluster_indices.add(new_index) new_cluster_indices = current_cluster_indices return list(cluster_indices)
def get_donor_vec(self, donor_index): """finds direction of lone pair electrons on an adsorbate donor atom :return: vector direction of donor atoms Args: donor_index: """ nl = NeighborList(natural_cutoffs(self), self_interaction=False, bothways=True) nl.update(self) # gets neighbors of donor atom and adds the vectors from neighbor to donor # for most donor atoms this is roughly in the proper binding direction donor_neighbors = nl.get_neighbors(donor_index)[0] # neighbor's index donor_vec = np.array([0, 0, 0]) for i in donor_neighbors: a = self.get_distance(i, donor_index, vector=True) donor_vec = donor_vec + a if np.linalg.norm(donor_vec) == 0: warnings.warn( "donor vector with magnitude 0 found, providing default vector" ) return np.array([1, 0, 0]) donor_vec = donor_vec / np.linalg.norm( donor_vec) # normalizes donor vec return donor_vec
def generate_normals_original(atoms, surface_normal=0.5, normalize_final=True, adsorbate_atoms=[]): normals = np.zeros(shape=(len(atoms), 3), dtype=float) atoms = atoms.copy() del atoms[adsorbate_atoms] cutoffs = natural_cutoffs(atoms) nl = NeighborList(cutoffs, self_interaction=False, bothways=True) nl.update(atoms) cell = atoms.get_cell() for index, atom in enumerate(atoms): normal = np.array([0, 0, 0], dtype=float) for neighbor, offset in zip(*nl.get_neighbors(index)): direction = atom.position - offset_position(atoms, neighbor, offset) normal += direction if norm(normal) > surface_normal: normals[index,:] = normalize(normal) if normalize_final else normal surface_mask = [index for index in range(len(atoms)) if norm(normals[index]) > 1e-5] return normals, surface_mask
def find_tmpo(atoms): """ Args: atoms: """ tmpo_indices = [] p_index = None for a in atoms: if a.symbol == 'P': p_index = a.index break tmpo_indices.append(p_index) if p_index is None: return tmpo_indices nl = NeighborList(natural_cutoffs(atoms), bothways=True, self_interaction=False) nl.update(atoms) p_nl = nl.get_neighbors(p_index)[0] tmpo_indices.extend(p_nl) for i in p_nl: if atoms[i].symbol == 'C': tmpo_indices.extend(nl.get_neighbors(i)[0]) return tmpo_indices
def xyz_to_json(mol): cutOff = neighborlist.natural_cutoffs(mol) cutOff = [cc - 0.2 for cc in cutOff] neighborList = neighborlist.NeighborList(cutOff, self_interaction=False, bothways=True) neighborList.update(mol) neighborList.get_neighbors(0) bmatrix = neighborList.get_connectivity_matrix(sparse=False) dmatrix = sp.spatial.distance_matrix(mol.positions, mol.positions) dmatrix[dmatrix > 2] == 0 g = nx.Graph(dmatrix) g2 = nx.Graph(bmatrix) list(g.edges(data=True)) symbols = mol.get_chemical_symbols() nodes_data = [{'id': node, 'atom': symbols[node]} for node in g.nodes] edge_data = [{ 'id': num, 'source': at[0], 'target': at[1], 'strength': 2 if (at[0], at[1]) in g2.edges() else 0.1, 'bond': 1 if (at[0], at[1]) in g2.edges() else 0, 'distance': at[2]['weight'] * 20 } for num, at in enumerate(list(g.edges(data=True)))] data = {'nodes': nodes_data, 'links': edge_data} return data
def create_connectivity_matrix(atoms, bothways): cutOff = neighborlist.natural_cutoffs(atoms) neighborList = neighborlist.NeighborList(cutOff, self_interaction=False, bothways=bothways) neighborList.update(atoms) connectivity_matrix = neighborList.get_connectivity_matrix() return connectivity_matrix
def get_rings(atoms, index): ''' WARNING: This is old and does not work for all framework types. WARNING: Use the updated get_orings function below instead. atoms: ASE atoms object of the zeolite framework to be analyzed index: (integer) index of the atom that you want to classify ''' cell = atoms.get_cell_lengths_and_angles()[:3] repeat = [] for i, c in enumerate(cell): if c / 2 < 15: l = c re = 1 while l / 2 < 15: re += 1 l = c * re repeat.append(re) else: repeat.append(1) atoms = atoms.repeat(repeat) center = atoms.get_center_of_mass() trans = center - atoms.positions[index] atoms.translate(trans) atoms.wrap() cutoff = neighborlist.natural_cutoffs(atoms, mult=1.05) nl = neighborlist.NeighborList(cutoffs=cutoff, self_interaction=False, bothways=True) nl.update(atoms) matrix = nl.get_connectivity_matrix(sparse=False) m = matrix.copy() G = nx.from_numpy_matrix(matrix) neighbs = nx.neighbors(G, index) for n in neighbs: if atoms[n].symbol == 'Si': fe = [n] fe.append(index) G.remove_edge(fe[0], fe[1]) Class = [] while len(Class) < 6: try: path = nx.shortest_path(G, fe[0], fe[1]) except: break Class.append(int(len(path) / 2)) for i in range(len(path) - 3): G.remove_edge(path[i + 1], path[i + 2]) Class.sort(reverse=True) return Class
def neighbours(atoms, centre, shell, cutoff=None, verbose=False): ''' Returns a list of indices of atomic neighbors from a central atom Parameters: atoms : Atoms object Input structure to count neighbours centre : list of integers Indices of atom(s) to start counting from shell : Integer Size of the nearest neighbour shell, 1st neighbours, 2nd neighbours .... cutoff: list of floats or None Bond length cutoff distance in Angstrom can be set for each atom individually The list must contain exactly len(atoms) floats. If None, natural_cutoffs are used by default. verbose: boolean If True, information about the cutoffs and selected neighbors is printed Returns: List of all atoms within specified neighbour distances List of lists containing indices of atoms that make 0th, 1st (...) shell ''' from ase.neighborlist import natural_cutoffs, NeighborList if not cutoff: # Choose a cutoff to determine max bond distance, otherwise use natural cutoff cutoff = natural_cutoffs(atoms) if verbose: print("Default bond cutoffs selected:", set([(atoms[i].symbol, cutoff[i]) for i in range(len(atoms))])) # Set object storing all neighbour information all_neighbours = set(centre) # List of lists storing each neighbour shell requested shell_list = [centre] # Creates an empty list an appends atom indices whose distances are # x amount nearest neighbours away from centre for neighbors in range(shell): # keep new neighbor indices in a set to avoid duplicates new_neighbors = set() for index in all_neighbours: # find neighbors based on cutoff and connectivity matrix nl = NeighborList(cutoff, self_interaction=False, bothways=True) nl.update(atoms) indices = nl.get_neighbors(index)[0] for i in indices: new_neighbors.add(i) shell_list += [[i for i in new_neighbors if i not in all_neighbours]] for i in new_neighbors: all_neighbours.add(i) if verbose: for shell in range(len(shell_list)): print("Shell", shell, "contains atoms with indices:", shell_list[shell]) return list(all_neighbours), shell_list
def construct(self, atoms, scale_factor=1.0): neighbor_list = build_neighbor_list(atoms, cutoffs=natural_cutoffs( atoms, mult=scale_factor), bothways=True, self_interaction=False) for atom_idx, _ in enumerate(atoms): neighbors, _ = neighbor_list.get_neighbors(atom_idx) self.list[atom_idx] = set(neighbors)
def update_nl(self, mult: float = 1) -> None: """ Builds and updates neighborlist :param mult: The mult (multiply) parameter for natural cutoffs (Default 1) :return: None """ self.neighbor_list = NeighborList(natural_cutoffs(self, mult=mult), bothways=True, self_interaction=False) self.neighbor_list.update(self)
def all_trings(atoms, index, possible, delete=True): ''' For developmental testing purposes only. ''' possible = possible * 2 atoms2 = atoms.copy() cell = atoms2.get_cell_lengths_and_angles()[:3] repeat = [] maxring = max(possible) for i, c in enumerate(cell): if c / 2 < maxring / 2 + 5: l = c re = 1 while l / 2 < maxring / 2 + 5: re += 1 l = c * re repeat.append(re) else: repeat.append(1) atoms2 = atoms2.repeat(repeat) center = atoms2.get_center_of_mass() trans = center - atoms2.positions[index] atoms2.translate(trans) atoms2.wrap() cutoff = neighborlist.natural_cutoffs(atoms2, mult=0.95) nl = neighborlist.NeighborList(cutoffs=cutoff, self_interaction=False, bothways=True) nl.update(atoms2) matrix = nl.get_connectivity_matrix(sparse=False) m = matrix.copy() G = nx.from_numpy_matrix(matrix) rings = find_simple_o_rings(G, index, possible) paths = remove_dups(rings) # paths = remove_sec(paths) if delete == True: keepers = [] for i in paths: for j in i: if j not in keepers: keepers.append(j) d = [atom.index for atom in atoms2 if atom.index not in keepers] atoms2 = substitute.tsub(atoms2, index, 'Al') del atoms2[d] Class = [] for p in paths: Class.append(int(len(p) / 2)) paths = [x for _, x in sorted(zip(Class, paths), reverse=True)] Class.sort(reverse=True) return Class, paths, atoms2, repeat
def type_atoms(atoms_obj): """ Args: atoms_obj: """ type_dict = defaultdict(list) # Getting neighbor list nl = NeighborList(natural_cutoffs(atoms_obj), bothways=True, self_interaction=False) nl.update(atoms_obj) for i in atoms_obj: # labels framework atoms if i.symbol in ['Sn', 'Al', 'Si']: label = 'framework-%s' % i.symbol # label carbon and nitrogen as adsorbate elif i.symbol in ['C', 'N']: label = 'adsorbate-%s' % i.symbol # label Cu, Ni as extraframework elif i.symbol in ['Cu', 'Ni']: label = 'extraframework-%s' % i.symbol # label oxygens as framework, adsorbate, or bound adsorbate elif i.symbol == 'O': neigh_ind = nl.get_neighbors(i.index) if len(neigh_ind) == 2: neigh_sym1, neigh_sym2 = atoms_obj[ neigh_ind[0][0]].symbol, atoms_obj[neigh_ind[0][1]].symbol # assign framework oxygens if neigh_sym1 in ['Sn', 'Al', 'Si' ] and neigh_sym2 in ['Sn', 'Al', 'Si']: label = 'framework-%s' % i.symbol # assign bound adsorbate oxygen elif neigh_sym1 in ['H', 'C', 'N' ] and neigh_sym2 in ['Sn', 'Al', 'Si']: label = 'bound-adsorbate-%s' % i.symbol elif neigh_sym2 in ['H', 'C', 'N' ] and neigh_sym1 in ['Sn', 'Al', 'Si']: label = 'bound-adsorbate-%s' % i.symbol # assign rest as adsorbate oxygen else: label = 'adsorbate-%s' % i.symbol # assign rest as adsorbate oxygen else: label = 'adsorbate-%s' % i.symbol # all others get 'none' type else: label = "None" type_dict[label].append(i.index) return dict(type_dict)
def attach_nl(self, cutoff=None, si=False, bw=True): if cutoff is None: coff = natural_cutoffs(self[0]) elif type(cutoff) is float: coff = np.full_like(self[0].numbers, cutoff) elif type(cutoff) is dict: coff = list(map(cutoff.get, self[0].numbers)) elif type(cutoff) is list: coff = cutoff self.nl = NeighborList(coff, skin=0.0, self_interaction=si, bothways=bw)
def generate_mask_indices_neightbor_list(supercellPristine, defectIndex, numLayers): # this function should be used before the defect was inserted # in type 1 and type 3 defect situation, the defect atom was removed, won't be able to calculate neightbor based an vacancy # generate and update the neightborList cutOff = natural_cutoffs(supercellPristine, 1) nl = neighborlist.NeighborList(cutOff, 0.3, False, True, True) nl.update(supercellPristine) # # takes out the symmetric connectivity matrix # matrix = nl.get_connectivity_matrix() # sets of indices totalSet = [] newSet = [] newNewSet = [] totalSet.append(defectIndex) newSet.append(defectIndex) # print totalSet # print newSet # print "=======" # grow starting from the defect, N layers for layer in range(numLayers): # for idx in newSet: # indices, offsets = nl.get_neighbors(idx) # newSet.remove(idx) # for idxx in indices: # if idxx not in totalSet: # totalSet.add(idxx) # newSet.add(idxx) # else: # pass while newSet: idx = newSet.pop() indices, offsets = nl.get_neighbors(idx) for idxx in indices: if idxx not in totalSet: totalSet.append(idxx) newNewSet.append(idxx) else: pass newSet = newNewSet newNewSet = [] # print totalSet # print newSet # print "=======" return totalSet
def get_all_Bronsted_sites(self, case_1Al=False, case_2Al=False, my_dict=None): """ This function samples all Bronsted sites for each T sites names (4 Bronsted sites for each T site). No double counting within each sites_name ('T1', 'T2', 'T3', etc), but do have overlaps among different site_names. """ if case_1Al is True: my_dict = copy.copy(self.dict_1Al_replaced) if case_2Al is True: my_dict = copy.copy(self.dict_2Al_replaced) dict_ZH = {} for site_name, all_zeo_with_same_T in my_dict.items(): if case_2Al is True: atoms = copy.copy(all_zeo_with_same_T) else: atoms = copy.copy(all_zeo_with_same_T[0]) nl = NeighborList(natural_cutoffs(atoms), bothways=True, self_interaction=False) nl.update(atoms) index_Al = [a.index for a in atoms if a.symbol == 'Al'] indices = [] for each_Al in index_Al: indices.append(list(nl.get_neighbors(each_Al)[0])) traj = [] if case_2Al is True: assert len(indices) == 2 all_pairs = [] for count, value1 in enumerate(indices[0]): all_pairs.extend([value1, value2] for value2 in indices[1]) assert len(all_pairs) == 16 for index_pair in all_pairs: new_atoms = copy.copy(atoms) for index in index_pair: new_atoms = self._insert_H(new_atoms, index) traj.append(new_atoms) else: for count, index in enumerate(indices[0]): new_atoms = self._insert_H(atoms, index) traj.append(new_atoms) dict_ZH[site_name] = traj return dict_ZH
def generate_site_graphs(atoms, full_graph, nl, sites, adsorbate_atoms=[], radius=3): cutoffs = natural_cutoffs(atoms) nl = NeighborList(cutoffs, self_interaction=False, bothways=True) nl.update(atoms) site_envs = [None] * len(sites) for index, site in enumerate(sites): new_site = process_site(atoms, full_graph, nl, site.cycle, radius=radius) site_envs[index] = [new_site] site.graph = site_envs[index] unique_envs, unique_sites = unique_chem_envs(site_envs, sites) return unique_sites
def checkBonded(clus): """ Check if every atom of the cluster is bonded to other """ cutOff = neighborlist.natural_cutoffs(clus, mult=1) neighborList = neighborlist.NeighborList(cutOff, self_interaction=False, bothways=True) neighborList.update(clus) matrix = neighborList.get_connectivity_matrix(sparse=False) n_components, component_list = sparse.csgraph.connected_components(matrix) if n_components == 1: bonded = True else: bonded = False return bonded
def _get_bonding(self, struct, natural_cutoff_mult=1, skin=0): # Use ASE neighborlist class to identify bonding of atoms atoms = struct.get_ase_atoms() cutOff = natural_cutoffs(atoms, mult=natural_cutoff_mult) neighborList = NeighborList(cutOff, self_interaction=False, bothways=True, skin=0) neighborList.update(atoms) # Construct bonding list indexed by atom in struct bonding_list = [[] for x in range(struct.geometry.shape[0])] for i in range(struct.geometry.shape[0]): bonding_list[i] = neighborList.get_neighbors(i)[0] return np.array(bonding_list)
def get_bonds(self, mult=1.20, skin=0.0, update=False): """ Returns array of covalent bonds in the molecule. In addition, these are stored in the Structure properties for future reference. Arguments --------- mult: float For ASE neighborlist skin: float For ASE neighborlist update: bool If True, will force an update of bond information. Returns ------- list The index of the list corresponds to the atom the bonds are describing. Inside each index is another list. This is the indices of the atoms the atom is bonded. Please keep in mind that Python iterables are zero indexed whereas most visualization softwares will label atoms starting with 1. """ if update == False and "bonds" in self.properties: pass else: atoms = self.get_ase_atoms() cutOff = natural_cutoffs(atoms, mult=mult) neighborList = NeighborList(cutOff, self_interaction=False, bothways=True, skin=skin) neighborList.update(atoms) # Construct bonding list indexed by atom in struct bonding_list = [[] for x in range(self.geometry.shape[0])] for i in range(self.geometry.shape[0]): temp_list = list(neighborList.get_neighbors(i)[0]) if len(temp_list) > 0: temp_list.sort() bonding_list[i] = [int(x) for x in temp_list] self.properties["bonds"] = bonding_list return self.properties["bonds"]
def atoms_to_graph(atoms, index, max_ring): ''' Helper function to repeat a unit cell enough times to capture the largest possible ring, and turn the new larger cell into a graph object. RETURNS: G = graph object representing zeolite framework in new larger cell large_atoms = ASE atoms object of the new larger cell framework repeat = array showing the number of times the cell was repeated: [x,y,z] ''' # first, repeat cell, center the cell, and wrap the atoms back into the cell cell = atoms.get_cell_lengths_and_angles()[:3] repeat = [] for i, c in enumerate(cell): if c / 2 < max_ring / 2 + 5: l = c re = 1 while l / 2 < max_ring / 2 + 5: re += 1 l = c * re repeat.append(re) else: repeat.append(1) large_atoms = atoms.copy() large_atoms = large_atoms.repeat(repeat) center = large_atoms.get_center_of_mass() trans = center - large_atoms.positions[index] large_atoms.translate(trans) large_atoms.wrap() # we need a neighborlist (connectivity matrix) before we can make our graph from ase import neighborlist cutoff = neighborlist.natural_cutoffs(large_atoms, mult=1.05) nl = neighborlist.NeighborList(cutoffs=cutoff, self_interaction=False, bothways=True) nl.update(large_atoms) matrix = nl.get_connectivity_matrix(sparse=False) # now we make the graph import networkx as nx G = nx.from_numpy_matrix(matrix) return G, large_atoms, repeat