def quick_view(structure, bonds=True, conventional=False, transform=None, show_box=True, bond_tol=0.2, stick_radius=0.1): """ A function to visualize pymatgen Structure objects in jupyter notebook using chemview package. Args: structure: pymatgen Structure bonds: (bool) visualize bonds. Bonds are found by comparing distances to added covalent radii of pairs. Defaults to True. conventional: (bool) use conventional cell. Defaults to False. transform: (list) can be used to make supercells with pymatgen.Structure.make_supercell method show_box: (bool) unit cell is shown. Defaults to True. bond_tol: (float) used if bonds=True. Sets the extra distance tolerance when finding bonds. stick_radius: (float) radius of bonds. Returns: A chemview.MolecularViewer object """ s = structure.copy() if conventional: s = SpacegroupAnalyzer(s).get_conventional_standard_structure() if transform: s.make_supercell(transform) atom_types = [i.symbol for i in s.species] if bonds: bonds = [] for i in range(s.num_sites - 1): sym_i = s[i].specie.symbol for j in range(i + 1, s.num_sites): sym_j = s[j].specie.symbol max_d = CovalentRadius.radius[sym_i] + CovalentRadius.radius[sym_j] + bond_tol if s.get_distance(i, j, np.array([0,0,0])) < max_d: bonds.append((i, j)) bonds = bonds if bonds else None mv = MolecularViewer(s.cart_coords, topology={'atom_types': atom_types, 'bonds': bonds}) if bonds: mv.ball_and_sticks(stick_radius=stick_radius) for i in s.sites: el = i.specie.symbol coord = i.coords r = CovalentRadius.radius[el] mv.add_representation('spheres', {'coordinates': coord.astype('float32'), 'colors': [get_atom_color(el)], 'radii': [r * 0.5], 'opacity': 1.0}) if show_box: o = np.array([0, 0, 0]) a, b, c = s.lattice.matrix[0], s.lattice.matrix[1], s.lattice.matrix[2] starts = [o, o, o, a, a, b, b, c, c, a + b, a + c, b + c] ends = [a, b, c, a + b, a + c, b + a, b + c, c + a, c + b, a + b + c, a + b + c, a + b + c] colors = [0xffffff for i in range(12)] mv.add_representation('lines', {'startCoords': np.array(starts), 'endCoords': np.array(ends), 'startColors': colors, 'endColors': colors}) return mv
def get_supercell(self, isprint=True): struc = self.apply_strain() c_struc = SpacegroupAnalyzer( struc).get_conventional_standard_structure() c_struc.make_supercell(self.scaling_list) if isprint: print('Your supercell has {} atoms in total\n'.format( len(c_struc.sites))) return c_struc
def find_dimension(structure_raw, tolerance=0.45, ldict=JmolNN().el_radius, standardize=True): """ Algorithm for finding the dimensions of connected subunits in a crystal structure. This method finds the dimensionality of the material even when the material is not layered along low-index planes, or does not have flat layers/molecular wires. See details at : Cheon, G.; Duerloo, K.-A. N.; Sendek, A. D.; Porter, C.; Chen, Y.; Reed, E. J. Data Mining for New Two- and One-Dimensional Weakly Bonded Solids and Lattice-Commensurate Heterostructures. Nano Lett. 2017. Args: structure (Structure): Input structure tolerance: length in angstroms used in finding bonded atoms. Two atoms are considered bonded if (radius of atom 1) + (radius of atom 2) + (tolerance) < (distance between atoms 1 and 2). Default value = 0.45, the value used by JMol and Cheon et al. ldict: dictionary of bond lengths used in finding bonded atoms. Values from JMol are used as default standardize: works with conventional standard structures if True. It is recommended to keep this as True. Returns: dim: dimension of the largest cluster as a string. If there are ions or molecules it returns 'intercalated ion/molecule' """ if standardize: structure = SpacegroupAnalyzer(structure_raw).get_conventional_standard_structure() structure_save = copy.copy(structure_raw) connected_list1 = find_connected_atoms(structure, tolerance=tolerance, ldict=ldict) max1, min1, clusters1 = find_clusters(structure, connected_list1) structure.make_supercell([[2, 0, 0], [0, 2, 0], [0, 0, 2]]) connected_list2 = find_connected_atoms(structure, tolerance=tolerance, ldict=ldict) max2, min2, clusters2 = find_clusters(structure, connected_list2) if min2 == 1: dim = 'intercalated ion' elif min2 == min1: if max2 == max1: dim = '0D' else: dim = 'intercalated molecule' else: dim = np.log2(float(max2) / max1) if dim == int(dim): dim = str(int(dim)) + 'D' else: structure = copy.copy(structure_save) structure.make_supercell([[3, 0, 0], [0, 3, 0], [0, 0, 3]]) connected_list3 = find_connected_atoms(structure, tolerance=tolerance, ldict=ldict) max3, min3, clusters3 = find_clusters(structure, connected_list3) if min3 == min2: if max3 == max2: dim = '0D' else: dim = 'intercalated molecule' else: dim = np.log2(float(max3) / max1) / np.log2(3) if dim == int(dim): dim = str(int(dim)) + 'D' else: return return dim
def find_dimension(structure_raw, tolerance=0.45, ldict=JmolNN().el_radius, standardize=True): """ Algorithm for finding the dimensions of connected subunits in a crystal structure. This method finds the dimensionality of the material even when the material is not layered along low-index planes, or does not have flat layers/molecular wires. See details at : Cheon, G.; Duerloo, K.-A. N.; Sendek, A. D.; Porter, C.; Chen, Y.; Reed, E. J. Data Mining for New Two- and One-Dimensional Weakly Bonded Solids and Lattice-Commensurate Heterostructures. Nano Lett. 2017. Args: structure (Structure): Input structure tolerance: length in angstroms used in finding bonded atoms. Two atoms are considered bonded if (radius of atom 1) + (radius of atom 2) + (tolerance) < (distance between atoms 1 and 2). Default value = 0.45, the value used by JMol and Cheon et al. ldict: dictionary of bond lengths used in finding bonded atoms. Values from JMol are used as default standardize: works with conventional standard structures if True. It is recommended to keep this as True. Returns: dim: dimension of the largest cluster as a string. If there are ions or molecules it returns 'intercalated ion/molecule' """ if standardize: structure = SpacegroupAnalyzer(structure_raw).get_conventional_standard_structure() structure_save = copy.copy(structure_raw) connected_list1 = find_connected_atoms(structure, tolerance=tolerance, ldict=ldict) max1, min1, clusters1 = find_clusters(structure, connected_list1) structure.make_supercell([[2, 0, 0], [0, 2, 0], [0, 0, 2]]) connected_list2 = find_connected_atoms(structure, tolerance=tolerance, ldict=ldict) max2, min2, clusters2 = find_clusters(structure, connected_list2) if min2 == 1: dim = 'intercalated ion' elif min2 == min1: if max2 == max1: dim = '0D' else: dim = 'intercalated molecule' else: dim = np.log2(float(max2) / max1) if dim == int(dim): dim = str(int(dim)) + 'D' else: structure=copy.copy(structure_save) structure.make_supercell([[3, 0, 0], [0, 3, 0], [0, 0, 3]]) connected_list3 = find_connected_atoms(structure, tolerance=tolerance, ldict=ldict) max3, min3, clusters3 = find_clusters(structure, connected_list3) if min3 == min2: if max3 == max2: dim = '0D' else: dim = 'intercalated molecule' else: dim = np.log2(float(max3) / max1) / np.log2(3) if dim == int(dim): dim = str(int(dim)) + 'D' else: return return dim
def cluster2(S, tol=1.1, seed_index=0, write_poscar_from_cluster=False): # S = Structure object # tol = tolerance bonding S = SpacegroupAnalyzer(S, 0.1).get_conventional_standard_structure() list_of_structures = [] if len(S) < 20: S.make_supercell(2) Distance_matrix = S.distance_matrix #np.fill_diagonal(Distance_matrix,100) radii = [Elem_radius[site.species_string] for site in S.sites] radiiT = np.array(radii)[np.newaxis].T #transpose of radii list radii_matrix = radii + radiiT * tol temp = Distance_matrix - radii_matrix binary_matrix = (temp < 0).astype(int) original = list(range(len(S))) final = [] difference = [] counter = 0 while (len(original) != len(final)): seed = set((np.where(binary_matrix[seed_index] == 1))[0]) cluster = seed NEW = seed check = True while check: Ss = set() for n in NEW: Ss.update(set(np.where(binary_matrix[n] == 1)[0])) #print n,set(np.where( binary_matrix[n]==1 )[0]) if Ss.issubset(cluster): check = False else: NEW = Ss - cluster cluster.update(Ss) L = [] cl = list(cluster) sites = [S[i] for i in cl] B = S.from_sites(sites) list_of_structures.append(B) counter += 1 final = set(final) final.update(cluster) final = list(final) difference = list(set(original) - set(final)) if (len(difference) > 0): seed_index = difference[0] return list_of_structures
def quick_view(structure: Structure, conventional: bool = False, supercell: list = None) -> JsmolView: """A function to visualize pymatgen Structure objects in jupyter notebook using jupyter_jsmol package. Args: structure: pymatgen Structure object. conventional: use conventional cell. Defaults to False. transform: can be used to make supercells with pymatgen.Structure.make_supercell method. Returns: A jupyter widget object. """ s = structure.copy() if conventional: s = SpacegroupAnalyzer(s).get_conventional_standard_structure() if supercell: s.make_supercell(supercell) return JsmolView.from_str(s.to('cif'))
def get_dimensionality_cheon( structure_raw, tolerance=0.45, ldict=JmolNN().el_radius, standardize=True, larger_cell=False, ): """ Algorithm for finding the dimensions of connected subunits in a structure. This method finds the dimensionality of the material even when the material is not layered along low-index planes, or does not have flat layers/molecular wires. Author: "Gowoon Cheon" Email: "*****@*****.**" See details at : Cheon, G.; Duerloo, K.-A. N.; Sendek, A. D.; Porter, C.; Chen, Y.; Reed, E. J. Data Mining for New Two- and One-Dimensional Weakly Bonded Solids and Lattice-Commensurate Heterostructures. Nano Lett. 2017. Args: structure_raw (Structure): A pymatgen Structure object. tolerance (float): length in angstroms used in finding bonded atoms. Two atoms are considered bonded if (radius of atom 1) + (radius of atom 2) + (tolerance) < (distance between atoms 1 and 2). Default value = 0.45, the value used by JMol and Cheon et al. ldict (dict): dictionary of bond lengths used in finding bonded atoms. Values from JMol are used as default standardize: works with conventional standard structures if True. It is recommended to keep this as True. larger_cell: tests with 3x3x3 supercell instead of 2x2x2. Testing with 2x2x2 supercell is faster but misclssifies rare interpenetrated 3D structures. Testing with a larger cell circumvents this problem Returns: (str): dimension of the largest cluster as a string. If there are ions or molecules it returns 'intercalated ion/molecule' """ if standardize: structure = SpacegroupAnalyzer( structure_raw).get_conventional_standard_structure() else: structure = structure_raw structure_save = copy.copy(structure_raw) connected_list1 = find_connected_atoms(structure, tolerance=tolerance, ldict=ldict) max1, min1, clusters1 = find_clusters(structure, connected_list1) if larger_cell: structure.make_supercell([[3, 0, 0], [0, 3, 0], [0, 0, 3]]) connected_list3 = find_connected_atoms(structure, tolerance=tolerance, ldict=ldict) max3, min3, clusters3 = find_clusters(structure, connected_list3) if min3 == min1: if max3 == max1: dim = "0D" else: dim = "intercalated molecule" else: dim = np.log2(float(max3) / max1) / np.log2(3) if dim == int(dim): dim = str(int(dim)) + "D" else: return None else: structure.make_supercell([[2, 0, 0], [0, 2, 0], [0, 0, 2]]) connected_list2 = find_connected_atoms(structure, tolerance=tolerance, ldict=ldict) max2, min2, clusters2 = find_clusters(structure, connected_list2) if min2 == 1: dim = "intercalated ion" elif min2 == min1: if max2 == max1: dim = "0D" else: dim = "intercalated molecule" else: dim = np.log2(float(max2) / max1) if dim == int(dim): dim = str(int(dim)) + "D" else: structure = copy.copy(structure_save) structure.make_supercell([[3, 0, 0], [0, 3, 0], [0, 0, 3]]) connected_list3 = find_connected_atoms(structure, tolerance=tolerance, ldict=ldict) max3, min3, clusters3 = find_clusters(structure, connected_list3) if min3 == min2: if max3 == max2: dim = "0D" else: dim = "intercalated molecule" else: dim = np.log2(float(max3) / max1) / np.log2(3) if dim == int(dim): dim = str(int(dim)) + "D" else: return None return dim
def get_structure_type(structure, tol=0.1, seed_index=0, write_poscar_from_cluster=False): """ This is a topology-scaling algorithm used to describe the periodicity of bonded clusters in a bulk structure. Args: structure (structure): Pymatgen structure object to classify. tol (float): Additional percent of atomic radii to allow for overlap, thereby defining bonds (0.1 = +10%, -0.1 = -10%) seed_index (int): Atom number to start the cluster. write_poscar_from_cluster (bool): Set to True to write a POSCAR file from the sites in the cluster. Returns: string. "molecular" (0D), "chain" (1D), "layered" (2D), or "conventional" (3D). Also includes " heterogeneous" if the cluster's composition is not equal to that of the overal structure. """ # Get conventional structure to orthogonalize the lattice as # much as possible. A tolerance of 0.1 Angst. was suggested by # pymatgen developers. s = SpacegroupAnalyzer(structure, 0.1).get_conventional_standard_structure() heterogeneous = False noble_gases = ["He", "Ne", "Ar", "Kr", "Xe", "Rn"] if len([e for e in structure.composition if e.symbol in noble_gases]) != 0: type = "noble gas" else: # make 2x2x2 supercell to ensure sufficient number of atoms # for cluster building. s.make_supercell(2) # Distance matrix (rowA, columnB) shows distance between # atoms A and B, taking PBCs into account. distance_matrix = s.distance_matrix # Fill diagonal with a large number, so the code knows that # each atom is not bonded to itself. np.fill_diagonal(distance_matrix, 100) # Rows (`radii`) and columns (`radiiT`) of radii. radii = [ELEMENT_RADII[site.species_string] for site in s.sites] radiiT = np.array(radii)[np.newaxis].T radii_matrix = radii + radiiT * (1 + tol) # elements of temp that have value less than 0 are bonded. temp = distance_matrix - radii_matrix # True (1) is placed where temp < 0, and False (0) where # it is not. binary_matrix = (temp < 0).astype(int) # list of atoms bonded to the seed atom of a cluster seed = set((np.where(binary_matrix[seed_index] == 1))[0]) cluster = seed NEW = seed while True: temp_set = set() for n in NEW: # temp_set will have all atoms, without duplicates, # that are connected to all atoms in NEW. temp_set.update(set(np.where(binary_matrix[n] == 1)[0])) if temp_set.issubset(cluster): # if temp_set has no new atoms, the search is done. break else: NEW = temp_set - cluster # List of newly discovered atoms cluster.update(temp_set) # cluster is updated with new atoms if len(cluster) == 0: # i.e. the cluster is a single atom. cluster = [seed_index] # Make sure it's not empty to write POSCAR. type = "molecular" elif len(cluster) == len(s.sites): # i.e. all atoms are bonded. type = "conventional" else: cmp = Composition.from_dict( Counter([s[l].specie.name for l in list(cluster)])) if cmp.reduced_formula != s.composition.reduced_formula: # i.e. the cluster does not have the same composition # as the overall crystal; therefore there are other # clusters of varying composition. heterogeneous = True old_cluster_size = len(cluster) # Increase structure to determine whether it is # layered or molecular, then perform the same kind # of cluster search as before. s.make_supercell(2) distance_matrix = s.distance_matrix np.fill_diagonal(distance_matrix, 100) radii = [ELEMENT_RADII[site.species_string] for site in s.sites] radiiT = np.array(radii)[np.newaxis].T radii_matrix = radii + radiiT * (1 + tol) temp = distance_matrix - radii_matrix binary_matrix = (temp < 0).astype(int) seed = set((np.where(binary_matrix[seed_index] == 1))[0]) cluster = seed NEW = seed check = True while check: temp_set = set() for n in NEW: temp_set.update(set(np.where(binary_matrix[n] == 1)[0])) if temp_set.issubset(cluster): check = False else: NEW = temp_set - cluster cluster.update(temp_set) if len(cluster) != 4 * old_cluster_size: type = "molecular" else: type = "layered" if heterogeneous: type += " heterogeneous" cluster_sites = [s.sites[n] for n in cluster] if write_poscar_from_cluster: s.from_sites(cluster_sites).get_primitive_structure().to( "POSCAR", "POSCAR") return type
def get_structure_type(structure, tol=0.1, seed_index=0, write_poscar_from_cluster=False): """ This is a topology-scaling algorithm used to describe the periodicity of bonded clusters in a bulk structure. Args: structure (structure): Pymatgen structure object to classify. tol (float): Additional percent of atomic radii to allow for overlap, thereby defining bonds (0.1 = +10%, -0.1 = -10%) seed_index (int): Atom number to start the cluster. write_poscar_from_cluster (bool): Set to True to write a POSCAR file from the sites in the cluster. Returns: string. "molecular" (0D), "chain" (1D), "layered" (2D), or "conventional" (3D). Also includes " heterogeneous" if the cluster's composition is not equal to that of the overal structure. """ # Get conventional structure to orthogonalize the lattice as # much as possible. A tolerance of 0.1 Angst. was suggested by # pymatgen developers. s = SpacegroupAnalyzer(structure, 0.1).get_conventional_standard_structure() heterogeneous = False noble_gases = ["He", "Ne", "Ar", "Kr", "Xe", "Rn"] if len([e for e in structure.composition if e.symbol in noble_gases]) != 0: type = "noble gas" else: # make 2x2x2 supercell to ensure sufficient number of atoms # for cluster building. s.make_supercell(2) # Distance matrix (rowA, columnB) shows distance between # atoms A and B, taking PBCs into account. distance_matrix = s.distance_matrix # Fill diagonal with a large number, so the code knows that # each atom is not bonded to itself. np.fill_diagonal(distance_matrix, 100) # Rows (`radii`) and columns (`radiiT`) of radii. radii = [ELEMENT_RADII[site.species_string] for site in s.sites] radiiT = np.array(radii)[np.newaxis].T radii_matrix = radii + radiiT*(1+tol) # elements of temp that have value less than 0 are bonded. temp = distance_matrix - radii_matrix # True (1) is placed where temp < 0, and False (0) where # it is not. binary_matrix = (temp < 0).astype(int) # list of atoms bonded to the seed atom of a cluster seed = set((np.where(binary_matrix[seed_index]==1))[0]) cluster = seed NEW = seed while True: temp_set = set() for n in NEW: # temp_set will have all atoms, without duplicates, # that are connected to all atoms in NEW. temp_set.update(set(np.where(binary_matrix[n]==1)[0])) if temp_set.issubset(cluster): # if temp_set has no new atoms, the search is done. break else: NEW = temp_set - cluster # List of newly discovered atoms cluster.update(temp_set) # cluster is updated with new atoms if len(cluster) == 0: # i.e. the cluster is a single atom. cluster = [seed_index] # Make sure it's not empty to write POSCAR. type = "molecular" elif len(cluster) == len(s.sites): # i.e. all atoms are bonded. type = "conventional" else: cmp = Composition.from_dict(Counter([s[l].specie.name for l in list(cluster)])) if cmp.reduced_formula != s.composition.reduced_formula: # i.e. the cluster does not have the same composition # as the overall crystal; therefore there are other # clusters of varying composition. heterogeneous = True old_cluster_size = len(cluster) # Increase structure to determine whether it is # layered or molecular, then perform the same kind # of cluster search as before. s.make_supercell(2) distance_matrix = s.distance_matrix np.fill_diagonal(distance_matrix,100) radii = [ELEMENT_RADII[site.species_string] for site in s.sites] radiiT = np.array(radii)[np.newaxis].T radii_matrix = radii + radiiT*(1+tol) temp = distance_matrix-radii_matrix binary_matrix = (temp < 0).astype(int) seed = set((np.where(binary_matrix[seed_index]==1))[0]) cluster = seed NEW = seed check = True while check: temp_set = set() for n in NEW: temp_set.update(set(np.where(binary_matrix[n]==1)[0])) if temp_set.issubset(cluster): check = False else: NEW = temp_set - cluster cluster.update(temp_set) if len(cluster) != 4 * old_cluster_size: type = "molecular" else: type = "layered" if heterogeneous: type += " heterogeneous" cluster_sites = [s.sites[n] for n in cluster] if write_poscar_from_cluster: s.from_sites(cluster_sites).get_primitive_structure().to("POSCAR", "POSCAR") return type
def get_structure_type(structure, write_poscar_from_cluster=False): """ This is a topology-scaling algorithm used to describe the periodicity of bonded clusters in a bulk structure. Args: structure (structure): Pymatgen structure object to classify. write_poscar_from_cluster (bool): Set to True to write a POSCAR from the sites in the cluster. Returns: string. 'molecular' (0D), 'chain', 'layered', 'heterogeneous' (intercalated 3D), or 'conventional' (3D) """ # The conventional standard structure is much easier to work # with. structure = SpacegroupAnalyzer( structure).get_conventional_standard_structure() # Noble gases don't have well-defined bonding radii. if len([ e for e in structure.composition if e.symbol in ['He', 'Ne', 'Ar', 'Kr', 'Xe'] ]) != 0: type = 'noble gas' else: if len(structure.sites) < 45: structure.make_supercell(2) # Create a dict of sites as keys and lists of their # bonded neighbors as values. sites = structure.sites bonds = {} for site in sites: bonds[site] = [] for i in range(len(sites)): site_1 = sites[i] for site_2 in sites[i + 1:]: if (site_1.distance(site_2) < float( Element(site_1.specie).atomic_radius + Element(site_2.specie).atomic_radius) * 1.1): bonds[site_1].append(site_2) bonds[site_2].append(site_1) # Assimilate all bonded atoms in a cluster; terminate # when it stops growing. cluster_terminated = False while not cluster_terminated: original_cluster_size = len(bonds[sites[0]]) for site in bonds[sites[0]]: bonds[sites[0]] += [ s for s in bonds[site] if s not in bonds[sites[0]] ] if len(bonds[sites[0]]) == original_cluster_size: cluster_terminated = True original_cluster = bonds[sites[0]] if len(bonds[sites[0]]) == 0: # i.e. the cluster is a single atom. type = 'molecular' elif len(bonds[sites[0]]) == len(sites): # i.e. all atoms are bonded. type = 'conventional' else: # If the cluster's composition is not equal to the # structure's overall composition, it is a heterogeneous # compound. cluster_composition_dict = {} for site in bonds[sites[0]]: if Element(site.specie) in cluster_composition_dict: cluster_composition_dict[Element(site.specie)] += 1 else: cluster_composition_dict[Element(site.specie)] = 1 uniform = True if len(cluster_composition_dict): cmp = Composition.from_dict(cluster_composition_dict) if cmp.reduced_formula != structure.composition.reduced_formula: uniform = False if not uniform: type = 'heterogeneous' else: # Make a 2x2x2 supercell and recalculate the # cluster's new size. If the new cluster size is # the same as the old size, it is a non-periodic # molecule. If it is 2x as big, it's a 1D chain. # If it's 4x as big, it is a layered material. old_cluster_size = len(bonds[sites[0]]) structure.make_supercell(2) sites = structure.sites bonds = {} for site in sites: bonds[site] = [] for i in range(len(sites)): site_1 = sites[i] for site_2 in sites[i + 1:]: if (site_1.distance(site_2) < float( Element(site_1.specie).atomic_radius + Element(site_2.specie).atomic_radius) * 1.1): bonds[site_1].append(site_2) bonds[site_2].append(site_1) cluster_terminated = False while not cluster_terminated: original_cluster_size = len(bonds[sites[0]]) for site in bonds[sites[0]]: bonds[sites[0]] += [ s for s in bonds[site] if s not in bonds[sites[0]] ] if len(bonds[sites[0]]) == original_cluster_size: cluster_terminated = True if len(bonds[sites[0]]) != 4 * old_cluster_size: type = 'molecular' else: type = 'layered' if write_poscar_from_cluster: Structure.from_sites(original_cluster).to('POSCAR', 'POSCAR') return type
sg_arr.append(sg) enp_arr.append(enp) fenp_arr.append(fenp) pf_arr.append(pf) bg_arr.append(bg) tm_arr.append(tm) ucf_arr.append(ucf) ehull_arr.append(ehull) scell_size = 1 for s in structures: svn=SpacegroupAnalyzer(s).get_conventional_standard_structure() a, b, c = s.lattice.abc svn.make_supercell([ceil(scell_size/a)+1, ceil(scell_size/b)+1, ceil(scell_size/c)+1]) structures_cvn.append(svn) parameters = {'atom_style': 'charge' ,'control_file':'/home/kamal/inelast.mod'} pair_styles = ['eam/alloy'] pair_coeff_files = [os.path.join(os.getcwd(), ff)] file=str(ff)+'_data.json' f=open(file,'w') f.write(json.dumps([dict(ucf=ucf,mp_id=mp,icsd=icsd,sg=sg,enp=enp,fenp=fenp,pf=pf,bg=bg,tm=tm,ehull=ehull) for ucf,mp,icsd,sg,enp,fenp,pf,bg,tm,ehull in zip(ucf_arr,mp_arr,icsd_arr,sg_arr,enp_arr,fenp_arr,pf_arr,bg_arr,tm_arr,ehull_arr)])) f.close() turn_knobs = OrderedDict( [ ('STRUCTURES', structures_cvn), ('PAIR_STYLE', pair_styles), ('PAIR_COEFF', pair_coeff_files) ] )
def quick_view(structure, bonds=True, conventional=False, transform=None, show_box=True, bond_tol=0.2, stick_radius=0.1): """ A function to visualize pymatgen Structure objects in jupyter notebook using chemview package. Args: structure: pymatgen Structure bonds: (bool) visualize bonds. Bonds are found by comparing distances to added covalent radii of pairs. Defaults to True. conventional: (bool) use conventional cell. Defaults to False. transform: (list) can be used to make supercells with pymatgen.Structure.make_supercell method show_box: (bool) unit cell is shown. Defaults to True. bond_tol: (float) used if bonds=True. Sets the extra distance tolerance when finding bonds. stick_radius: (float) radius of bonds. Returns: A chemview.MolecularViewer object """ s = structure.copy() if conventional: s = SpacegroupAnalyzer(s).get_conventional_standard_structure() if transform: s.make_supercell(transform) atom_types = [i.symbol for i in s.species] if bonds: bonds = [] for i in range(s.num_sites - 1): sym_i = s[i].specie.symbol for j in range(i + 1, s.num_sites): sym_j = s[j].specie.symbol max_d = CovalentRadius.radius[sym_i] + CovalentRadius.radius[ sym_j] + bond_tol if s.get_distance(i, j, np.array([0, 0, 0])) < max_d: bonds.append((i, j)) bonds = bonds if bonds else None mv = MolecularViewer(s.cart_coords, topology={ 'atom_types': atom_types, 'bonds': bonds }) if bonds: mv.ball_and_sticks(stick_radius=stick_radius) for i in s.sites: el = i.specie.symbol coord = i.coords r = CovalentRadius.radius[el] mv.add_representation( 'spheres', { 'coordinates': coord.astype('float32'), 'colors': [get_atom_color(el)], 'radii': [r * 0.5], 'opacity': 1.0 }) if show_box: o = np.array([0, 0, 0]) a, b, c = s.lattice.matrix[0], s.lattice.matrix[1], s.lattice.matrix[2] starts = [o, o, o, a, a, b, b, c, c, a + b, a + c, b + c] ends = [ a, b, c, a + b, a + c, b + a, b + c, c + a, c + b, a + b + c, a + b + c, a + b + c ] colors = [0xffffff for i in range(12)] mv.add_representation( 'lines', { 'startCoords': np.array(starts), 'endCoords': np.array(ends), 'startColors': colors, 'endColors': colors }) return mv
def display_structure(structure, ax, miller_index=None, rotate=0, repeat=None, transform_to_conventional=False, repeat_unitcell_edge_atoms=True, draw_unit_cell=True, draw_frame=False, draw_legend=True, legend_loc='best', legend_fontsize=14, padding=5.0, scale=0.8, decay=0.0): """ Function that helps visualize the struct in a 2-D matplotlib plot Args: structure (struct): struct object to be visualized ax (axes): matplotlib axes with which to visualize miller_index (list): Viewing direction (normal to Miller plane) rotate (float): Rotate view around the viewing direction (Miller index normal stays the same, just rotation within plane normal to Miller index) repeat (list): number of repeating unit cells in x,y,z to visualize transform_to_conventional (bool): Whether to transform input structure to conventional centering repeat_unitcell_edge_atoms (bool): Whether to repeat atoms that lie on the edges/corners of unit cell_length (makes the visualization look cut off more smoothly) draw_unit_cell (bool): flag indicating whether or not to draw cell boundaries draw_frame (bool): Whether to draw a frame around the plot (axis on/off) draw_legend (bool): Whether to draw a legend labeling the atom types legend_loc (string): Location of legend legend_fontsize (int): Fontsize of legend padding (float): Padding of the plot around the outermost atoms scale (float): radius scaling for sites decay (float): how the alpha-value decays along the z-axis """ if miller_index is None: miller_index = [1, 0, 0] if repeat is None: repeat = [1, 1, 1] struct = structure.copy() if transform_to_conventional: struct = SpacegroupAnalyzer( struct).get_conventional_standard_structure() if repeat_unitcell_edge_atoms: struct = repeat_uc_edge_atoms(struct) struct = reorient(struct, miller_index, rotate=rotate) orig_cell = struct.lattice.matrix.copy() if repeat: struct.make_supercell(repeat) coords = np.array(sorted(struct.cart_coords, key=lambda x: x[2])) sites = sorted(struct.sites, key=lambda x: x.coords[2]) alphas = 1 - decay * (np.max(coords[:, 2]) - coords[:, 2]) alphas = alphas.clip(min=0) # Draw circles at sites and stack them accordingly for n, coord in enumerate(coords): r = sites[n].specie.atomic_radius * scale ax.add_patch(Circle(coord[:2], r, color='w', zorder=2 * n)) color = color_dict[sites[n].species_string] ax.add_patch( Circle(coord[:2], r, facecolor=color, alpha=alphas[n], edgecolor='k', lw=1, zorder=2 * n + 1)) # Draw unit cell if draw_unit_cell: a, b, c = orig_cell[0], orig_cell[1], orig_cell[2] n = np.array([0, 0, 1]) # Draw basis vectors as arrows proj_a, proj_b, proj_c = a - np.dot(a, n) * n, b - np.dot( b, n) * n, c - np.dot(c, n) * n if (proj_a[0]**2 + proj_a[1]**2)**0.5 > 0.5: verts = [[0., 0.], proj_a[:2]] codes = [Path.MOVETO, Path.LINETO] path = Path(verts, codes) patch = FancyArrowPatch( path=path, arrowstyle="-|>,head_length=7,head_width=4", facecolor='black', lw=2, alpha=1, zorder=500) ax.add_patch(patch) if (proj_b[0]**2 + proj_b[1]**2)**0.5 > 0.5: verts = [[0., 0.], proj_b[:2]] codes = [Path.MOVETO, Path.LINETO] path = Path(verts, codes) patch = FancyArrowPatch( path=path, arrowstyle="-|>,head_length=7,head_width=4", facecolor='black', lw=2, alpha=1, zorder=500) ax.add_patch(patch) if (proj_c[0]**2 + proj_c[1]**2)**0.5 > 0.5: verts = [[0., 0.], proj_c[:2]] codes = [Path.MOVETO, Path.LINETO] path = Path(verts, codes) patch = FancyArrowPatch( path=path, arrowstyle="-|>,head_length=7,head_width=4", facecolor='black', lw=2, alpha=1, zorder=500) ax.add_patch(patch) # Draw opposing three unit cell boundaries abc = a + b + c - np.dot(a + b + c, n) * n ab = a + b - np.dot(a + b, n) * n ac = a + c - np.dot(a + c, n) * n bc = b + c - np.dot(b + c, n) * n verts_top = [abc[:2], ab[:2], abc[:2], ac[:2], abc[:2], bc[:2]] codes_top = [ Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO ] path_top = Path(verts_top, codes_top) patch_top = patches.PathPatch(path_top, facecolor='none', lw=2, alpha=0.5, zorder=500) ax.add_patch(patch_top) # Draw remaining surrounding unit cell boundaries verts_surround = [ proj_a[:2], ac[:2], proj_c[:2], bc[:2], proj_b[:2], ab[:2], proj_a[:2] ] codes_surround = [ Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO ] path_surround = Path(verts_surround, codes_surround) patch_surround = patches.PathPatch(path_surround, facecolor='none', lw=2, alpha=0.5, zorder=500) ax.add_patch(patch_surround) # Legend if draw_legend: unique_sites = list({s.species_string for s in sites}) unique_colors = [color_dict[site] for site in unique_sites] handles = [ Patch(facecolor=unique_colors[i], label=str(unique_sites[i]), linewidth=1, edgecolor='black') for i in range(len(unique_sites)) ] ax.legend(handles=handles, frameon=False, loc=legend_loc, fontsize=legend_fontsize) ax.set_aspect("equal") x_lim = [min(coords[:, 0]) - padding, max(coords[:, 0]) + padding] y_lim = [min(coords[:, 1]) - padding, max(coords[:, 1]) + padding] ax.set_xlim(x_lim) ax.set_ylim(y_lim) if not draw_frame: ax.axis('off') ax.set_xticks([]) ax.set_yticks([]) return ax
enp_arr.append(enp) fenp_arr.append(fenp) pf_arr.append(pf) bg_arr.append(bg) tm_arr.append(tm) ucf_arr.append(ucf) ehull_arr.append(ehull) scell_size = 1 for s in structures: svn = SpacegroupAnalyzer( s).get_conventional_standard_structure() a, b, c = s.lattice.abc svn.make_supercell([ ceil(scell_size / a) + 1, ceil(scell_size / b) + 1, ceil(scell_size / c) + 1 ]) structures_cvn.append(svn) parameters = { 'atom_style': 'charge', 'control_file': '/home/kamal/inelast.mod' } pair_styles = ['eam/alloy'] pair_coeff_files = [os.path.join(os.getcwd(), ff)] file = str(ff) + '_data.json' f = open(file, 'w') f.write( json.dumps([ dict(ucf=ucf, mp_id=mp,
def get_structure_type(structure, write_poscar_from_cluster=False): """ This is a topology-scaling algorithm used to describe the periodicity of bonded clusters in a bulk structure. Args: structure (structure): Pymatgen structure object to classify. write_poscar_from_cluster (bool): Set to True to write a POSCAR from the sites in the cluster. Returns: string. 'molecular' (0D), 'chain', 'layered', 'heterogeneous' (intercalated 3D), or 'conventional' (3D) """ # The conventional standard structure is much easier to work # with. structure = SpacegroupAnalyzer( structure).get_conventional_standard_structure() # Noble gases don't have well-defined bonding radii. if not len([e for e in structure.composition if e.symbol in ['He', 'Ne', 'Ar', 'Kr', 'Xe']]) == 0: type = 'noble gas' else: if len(structure.sites) < 45: structure.make_supercell(2) # Create a dict of sites as keys and lists of their # bonded neighbors as values. sites = structure.sites bonds = {} for site in sites: bonds[site] = [] for i in range(len(sites)): site_1 = sites[i] for site_2 in sites[i+1:]: if (site_1.distance(site_2) < float(Element(site_1.specie).atomic_radius + Element(site_2.specie).atomic_radius) * 1.1): bonds[site_1].append(site_2) bonds[site_2].append(site_1) # Assimilate all bonded atoms in a cluster; terminate # when it stops growing. cluster_terminated = False while not cluster_terminated: original_cluster_size = len(bonds[sites[0]]) for site in bonds[sites[0]]: bonds[sites[0]] += [ s for s in bonds[site] if s not in bonds[sites[0]]] if len(bonds[sites[0]]) == original_cluster_size: cluster_terminated = True original_cluster = bonds[sites[0]] if len(bonds[sites[0]]) == 0: # i.e. the cluster is a single atom. type = 'molecular' elif len(bonds[sites[0]]) == len(sites): # i.e. all atoms are bonded. type = 'conventional' else: # If the cluster's composition is not equal to the # structure's overall composition, it is a heterogeneous # compound. cluster_composition_dict = {} for site in bonds[sites[0]]: if Element(site.specie) in cluster_composition_dict: cluster_composition_dict[Element(site.specie)] += 1 else: cluster_composition_dict[Element(site.specie)] = 1 uniform = True if len(cluster_composition_dict): cmp = Composition.from_dict(cluster_composition_dict) if cmp.reduced_formula != structure.composition.reduced_formula: uniform = False if not uniform: type = 'heterogeneous' else: # Make a 2x2x2 supercell and recalculate the # cluster's new size. If the new cluster size is # the same as the old size, it is a non-periodic # molecule. If it is 2x as big, it's a 1D chain. # If it's 4x as big, it is a layered material. old_cluster_size = len(bonds[sites[0]]) structure.make_supercell(2) sites = structure.sites bonds = {} for site in sites: bonds[site] = [] for i in range(len(sites)): site_1 = sites[i] for site_2 in sites[i+1:]: if (site_1.distance(site_2) < float(Element(site_1.specie).atomic_radius + Element(site_2.specie).atomic_radius) * 1.1): bonds[site_1].append(site_2) bonds[site_2].append(site_1) cluster_terminated = False while not cluster_terminated: original_cluster_size = len(bonds[sites[0]]) for site in bonds[sites[0]]: bonds[sites[0]] += [ s for s in bonds[site] if s not in bonds[sites[0]]] if len(bonds[sites[0]]) == original_cluster_size: cluster_terminated = True if len(bonds[sites[0]]) != 4 * old_cluster_size: type = 'molecular' else: type = 'layered' if write_poscar_from_cluster: Structure.from_sites(original_cluster).to('POSCAR', 'POSCAR') return type