def determine_symmetry_positions(st, element): """ determine non-equivalent positions for atoms of type *element* element (str) - name of element, for example Li return list of lists - atom numbers for each non-equivalent position """ from pymatgen.symmetry.analyzer import SpacegroupAnalyzer stp = st.convert2pymatgen() spg = SpacegroupAnalyzer(stp) info = spg.get_symmetry_dataset() positions = {} for i, (el, pos) in enumerate(zip(st.get_elements(), info['equivalent_atoms'])): if el == element and pos not in positions: positions[pos] = [] if el == element: positions[pos].append(i) printlog('I have found ', len(positions), 'non-equivalent positions for', element, ':',positions.keys(), imp = 'y', end = '\n') printlog('Atom numbers: ', positions, imp = 'y') sorted_keys = sorted(list(positions.keys())) pos_lists = [positions[key] for key in sorted_keys ] return pos_lists
def symmetry_reduce(tensors, structure, tol=1e-8, **kwargs): """ Function that converts a list of tensors corresponding to a structure and returns a dictionary consisting of unique tensor keys with symmop values corresponding to transformations that will result in derivative tensors from the original list Args: tensors (list of tensors): list of Tensor objects to test for symmetrically-equivalent duplicates structure (Structure): structure from which to get symmetry tol (float): tolerance for tensor equivalence kwargs: keyword arguments for the SpacegroupAnalyzer returns: dictionary consisting of unique tensors with symmetry operations corresponding to those which will reconstruct the remaining tensors as values """ sga = SpacegroupAnalyzer(structure, **kwargs) symmops = sga.get_symmetry_operations(cartesian=True) unique_tdict = {} for tensor in tensors: is_unique = True for unique_tensor, symmop in itertools.product(unique_tdict, symmops): if (np.abs(unique_tensor.transform(symmop) - tensor) < tol).all(): unique_tdict[unique_tensor].append(symmop) is_unique = False break if is_unique: unique_tdict[tensor] = [] return unique_tdict
def test_partial_disorder(self): s = Structure.from_file(filename=os.path.join(test_dir, "garnet.cif")) a = SpacegroupAnalyzer(s, 0.1) prim = a.find_primitive() s = prim.copy() s["Al3+"] = {"Al3+": 0.5, "Ga3+": 0.5} adaptor = EnumlibAdaptor(s, 1, 1, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 7) for s in structures: self.assertEqual(s.formula, 'Ca12 Al4 Ga4 Si12 O48') s = prim.copy() s["Ca2+"] = {"Ca2+": 1/3, "Mg2+": 2/3} adaptor = EnumlibAdaptor(s, 1, 1, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 20) for s in structures: self.assertEqual(s.formula, 'Ca4 Mg8 Al8 Si12 O48') s = prim.copy() s["Si4+"] = {"Si4+": 1/3, "Ge4+": 2/3} adaptor = EnumlibAdaptor(s, 1, 1, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 18) for s in structures: self.assertEqual(s.formula, 'Ca12 Al8 Si4 Ge8 O48')
def multiplicity(self): """ Returns the multiplicity of a defect site within the structure (needed for concentration analysis) """ if self._multiplicity is None: # generate multiplicity based on space group symmetry operations performed on defect coordinates try: d_structure = create_saturated_interstitial_structure(self) except ValueError: logger.debug('WARNING! Multiplicity was not able to be calculated adequately ' 'for interstitials...setting this to 1 and skipping for now...') return 1 sga = SpacegroupAnalyzer(d_structure) periodic_struc = sga.get_symmetrized_structure() poss_deflist = sorted( periodic_struc.get_sites_in_sphere(self.site.coords, 2, include_index=True), key=lambda x: x[1]) defindex = poss_deflist[0][2] equivalent_sites = periodic_struc.find_equivalent_sites(periodic_struc[defindex]) return len(equivalent_sites) else: return self._multiplicity
def symm_check(self, ucell, wulff_vertices): """ # Checks if the point group of the Wulff shape matches # the point group of its conventional unit cell Args: ucell (string): Unit cell that the Wulff shape is based on. wulff_vertices (list): List of all vertices on the Wulff shape. Use wulff.wulff_pt_list to obtain the list (see wulff_generator.py). return (bool) """ space_group_analyzer = SpacegroupAnalyzer(ucell) symm_ops = space_group_analyzer.get_point_group_operations( cartesian=True) for point in wulff_vertices: for op in symm_ops: symm_point = op.operate(point) if in_coord_list(wulff_vertices, symm_point): continue else: return False return True
def test_structure_transform(self): # Test trivial case trivial = self.fit_r4.structure_transform(self.structure, self.structure.copy()) self.assertArrayAlmostEqual(trivial, self.fit_r4) # Test simple rotation rot_symm_op = SymmOp.from_axis_angle_and_translation([1, 1, 1], 55.5) rot_struct = self.structure.copy() rot_struct.apply_operation(rot_symm_op) rot_tensor = self.fit_r4.rotate(rot_symm_op.rotation_matrix) trans_tensor = self.fit_r4.structure_transform(self.structure, rot_struct) self.assertArrayAlmostEqual(rot_tensor, trans_tensor) # Test supercell bigcell = self.structure.copy() bigcell.make_supercell([2, 2, 3]) trans_tensor = self.fit_r4.structure_transform(self.structure, bigcell) self.assertArrayAlmostEqual(self.fit_r4, trans_tensor) # Test rotated primitive to conventional for fcc structure sn = self.get_structure("Sn") sn_prim = SpacegroupAnalyzer(sn).get_primitive_standard_structure() sn_prim.apply_operation(rot_symm_op) rotated = self.fit_r4.rotate(rot_symm_op.rotation_matrix) transformed = self.fit_r4.structure_transform(sn, sn_prim) self.assertArrayAlmostEqual(rotated, transformed)
def test_apply_transformation(self): trans = MagOrderingTransformation({"Fe": 5}) p = Poscar.from_file(os.path.join(test_dir, 'POSCAR.LiFePO4'), check_for_POTCAR=False) s = p.structure alls = trans.apply_transformation(s, 10) self.assertEqual(len(alls), 3) f = SpacegroupAnalyzer(alls[0]["structure"], 0.1) self.assertEqual(f.get_space_group_number(), 31) model = IsingModel(5, 5) trans = MagOrderingTransformation({"Fe": 5}, energy_model=model) alls2 = trans.apply_transformation(s, 10) # Ising model with +J penalizes similar neighbor magmom. self.assertNotEqual(alls[0]["structure"], alls2[0]["structure"]) self.assertEqual(alls[0]["structure"], alls2[2]["structure"]) s = self.get_structure('Li2O') # Li2O doesn't have magnetism of course, but this is to test the # enumeration. trans = MagOrderingTransformation({"Li+": 1}, max_cell_size=3) alls = trans.apply_transformation(s, 100) # TODO: check this is correct, unclear what len(alls) should be self.assertEqual(len(alls), 12) trans = MagOrderingTransformation({"Ni": 5}) alls = trans.apply_transformation(self.NiO.get_primitive_structure(), return_ranked_list=10) self.assertEqual(self.NiO_AFM_111.lattice, alls[0]["structure"].lattice) self.assertEqual(self.NiO_AFM_001.lattice, alls[1]["structure"].lattice)
def set_space_group_from_structure(self, structure): spga = SpacegroupAnalyzer(structure=structure) self.crystal_system = spga.get_crystal_system() self.hall = spga.get_hall() self.number = spga.get_space_group_number() self.source = "spglib" self.symbol = spga.get_space_group_symbol()
def get_unique_site_indices(structure): sa = SpacegroupAnalyzer(structure) symm_data = sa.get_symmetry_dataset() # equivalency mapping for the structure # i'th site in the struct equivalent to eq_struct[i]'th site eq_atoms = symm_data["equivalent_atoms"] return np.unique(eq_atoms).tolist()
def symmetrize(self, structure): tensor = self._reduced_tensor if self._is_real_space: real_lattice = self._lattice else: real_lattice = self._lattice.reciprocal_lattice # I guess this is the reason why tensor.symmetrize (omega) is so slow! real_finder = SpacegroupAnalyzer(structure) real_symmops = real_finder.get_point_group_operations(cartesian=True) cartesian_tensor = self.cartesian_tensor sym_tensor = np.zeros((3,3)) my_tensor = cartesian_tensor for real_sym in real_symmops: mat = real_sym.rotation_matrix prod_sym = np.dot(np.transpose(mat),np.dot(cartesian_tensor,mat)) sym_tensor = sym_tensor + prod_sym sym_tensor = sym_tensor/len(real_symmops) self._reduced_tensor = from_cart_to_red(sym_tensor,self._lattice)
def __init__(self, structure, min_cell_size=1, max_cell_size=1, symm_prec=0.1, enum_precision_parameter=0.001, refine_structure=False): """ Initializes the adapter with a structure and some parameters. Args: structure: An input structure. min_cell_size (int): The minimum cell size wanted. Defaults to 1. max_cell_size (int): The maximum cell size wanted. Defaults to 1. symm_prec (float): Symmetry precision. Defaults to 0.1. enum_precision_parameter (float): Finite precision parameter for enumlib. Default of 0.001 is usually ok, but you might need to tweak it for certain cells. refine_structure (bool): If you are starting from a structure that has been relaxed via some electronic structure code, it is usually much better to start with symmetry determination and then obtain a refined structure. The refined structure have cell parameters and atomic positions shifted to the expected symmetry positions, which makes it much less sensitive precision issues in enumlib. If you are already starting from an experimental cif, refinement should have already been done and it is not necessary. Defaults to False. """ if refine_structure: finder = SpacegroupAnalyzer(structure, symm_prec) self.structure = finder.get_refined_structure() else: self.structure = structure self.min_cell_size = min_cell_size self.max_cell_size = max_cell_size self.symm_prec = symm_prec self.enum_precision_parameter = enum_precision_parameter
def __init__(self, structure, element): """ Initializes a Substitution Generator note: an Antisite is considered a type of substitution Args: structure(Structure): pymatgen structure object element (str or Element or Specie): element for the substitution """ self.structure = structure self.element = element # Find equivalent site list sga = SpacegroupAnalyzer(self.structure) self.symm_structure = sga.get_symmetrized_structure() self.equiv_sub = [] for equiv_site_set in list(self.symm_structure.equivalent_sites): vac_site = equiv_site_set[0] if isinstance(element, str): # make sure you compare with specie symbol or Element type vac_specie = vac_site.specie.symbol else: vac_specie = vac_site.specie if element != vac_specie: defect_site = PeriodicSite(element, vac_site.coords, structure.lattice, coords_are_cartesian=True) sub = Substitution(structure, defect_site) self.equiv_sub.append(sub)
def print_spg(src="POSCAR"): srcpos = Poscar.from_file(src) finder = SpacegroupAnalyzer(srcpos.structure, symprec=5e-2, angle_tolerance=8) spg = finder.get_spacegroup_symbol() spg_num = finder.get_spacegroup_number() print(spg) print(spg_num)
def __init__(self, structure, element): """ Initializes an Interstitial generator using Voronoi sites Args: structure (Structure): pymatgen structure object element (str or Element or Specie): element for the interstitial """ self.structure = structure self.element = element framework = list(self.structure.symbol_set) get_voronoi = TopographyAnalyzer(self.structure, framework, [], check_volume=False) get_voronoi.cluster_nodes() get_voronoi.remove_collisions() # trim equivalent nodes with symmetry analysis struct_to_trim = self.structure.copy() for poss_inter in get_voronoi.vnodes: struct_to_trim.append(self.element, poss_inter.frac_coords, coords_are_cartesian=False) symmetry_finder = SpacegroupAnalyzer(struct_to_trim, symprec=1e-1) equiv_sites_list = symmetry_finder.get_symmetrized_structure().equivalent_sites self.equiv_site_seq = [] for poss_site_list in equiv_sites_list: if poss_site_list[0] not in self.structure: self.equiv_site_seq.append(poss_site_list) self.count_def = 0 # for counting the index of the generated defect
def test_tensor(self): """Initialize Tensor""" lattice = Lattice.hexagonal(4,6) #rprimd = np.array([[0,0.5,0.5],[0.5,0,0.5],[0.5,0.5,0]]) #rprimd = rprimd*10 #lattice = Lattice(rprimd) structure = Structure(lattice, ["Ga", "As"], [[0, 0, 0], [0.5, 0.5, 0.5]]) #finder = SymmetryFinder(structure) finder = SpacegroupAnalyzer(structure) spacegroup = finder.get_spacegroup() pointgroup = finder.get_point_group() cartesian_tensor = [[2,3,1.2],[3,4,1.0],[1.2,1.0,6]] tensor = Tensor.from_cartesian_tensor(cartesian_tensor,lattice.reciprocal_lattice,space="g") red_tensor = tensor.reduced_tensor tensor2 = Tensor(red_tensor,lattice.reciprocal_lattice,space="g") assert(((np.abs(tensor2.cartesian_tensor)-np.abs(cartesian_tensor)) < 1E-8).all()) self.assertTrue(tensor==tensor2) print(tensor) #print("non-symmetrized cartesian_tensor = ",tensor2.cartesian_tensor) tensor2.symmetrize(structure) #print("symmetrized_cartesian_tensor = ",tensor2.cartesian_tensor) self.serialize_with_pickle(tensor)
def test_adsorb_both_surfaces(self): # Test out for monatomic adsorption o = Molecule("O", [[0, 0, 0]]) adslabs = self.asf_100.adsorb_both_surfaces(o) adslabs_one = self.asf_100.generate_adsorption_structures(o) self.assertEqual(len(adslabs), len(adslabs_one)) for adslab in adslabs: sg = SpacegroupAnalyzer(adslab) sites = sorted(adslab, key=lambda site: site.frac_coords[2]) self.assertTrue(sites[0].species_string == "O") self.assertTrue(sites[-1].species_string == "O") self.assertTrue(sg.is_laue()) # Test out for molecular adsorption oh = Molecule(["O", "H"], [[0, 0, 0], [0, 0, 1]]) adslabs = self.asf_100.adsorb_both_surfaces(oh) adslabs_one = self.asf_100.generate_adsorption_structures(oh) self.assertEqual(len(adslabs), len(adslabs_one)) for adslab in adslabs: sg = SpacegroupAnalyzer(adslab) sites = sorted(adslab, key=lambda site: site.frac_coords[2]) self.assertTrue(sites[0].species_string in ["O", "H"]) self.assertTrue(sites[-1].species_string in ["O", "H"]) self.assertTrue(sg.is_laue())
def from_structure(cls, structure, has_timerev=True, symprec=1e-5, angle_tolerance=5): """ Takes a :class:`Structure` object. Uses spglib to perform various symmetry finding operations. Args: structure: :class:`Structure` object has_timerev: True is time-reversal symmetry is included. symprec: Tolerance for symmetry finding angle_tolerance: Angle tolerance for symmetry finding. .. warning:: AFM symmetries are not supported. """ # Call spglib to get the list of symmetry operations. spga = SpacegroupAnalyzer(structure, symprec=symprec, angle_tolerance=angle_tolerance) data = spga.get_symmetry_dataset() return cls( spgid=data["number"], symrel=data["rotations"], tnons=data["translations"], symafm=len(symrel) * [1], has_timerev=has_timerev, inord="C", )
def get_sym_eq_kpoints(self, kpoint, cartesian=False, tol=1e-2): """ Returns a list of unique symmetrically equivalent k-points. Args: kpoint (1x3 array): coordinate of the k-point cartesian (bool): kpoint is in cartesian or fractional coordinates tol (float): tolerance below which coordinates are considered equal Returns: ([1x3 array] or None): if structure is not available returns None """ if not self.structure: return None sg = SpacegroupAnalyzer(self.structure) symmops = sg.get_point_group_operations(cartesian=cartesian) points = np.dot(kpoint, [m.rotation_matrix for m in symmops]) rm_list = [] # identify and remove duplicates from the list of equivalent k-points: for i in range(len(points) - 1): for j in range(i + 1, len(points)): if np.allclose(pbc_diff(points[i], points[j]), [0, 0, 0], tol): rm_list.append(i) break return np.delete(points, rm_list, axis=0)
def unique_symmetry_operations_as_vectors_from_structure( structure, subset = None ): """ Uses pymatgen symmetry analysis to find the minimum complete set of symmetry operations for the space group of a structure. Args: structure (pymatgen Structure): structure to be analysed. subset (Optional [list]): list of atom indices to be used for generating the symmetry operations Returns: a list of lists, containing the symmetry operations as vector mappings. """ symmetry_analyzer = SpacegroupAnalyzer( structure ) print( "The spacegroup for structure is {}".format(symmetry_analyzer.get_spacegroup_symbol()) ) symmetry_operations = symmetry_analyzer.get_symmetry_operations() mappings = [] if subset: species_subset = [ spec for i,spec in enumerate(structure.species) if i in subset] frac_coords_subset = [ coord for i, coord in enumerate( structure.frac_coords ) if i in subset ] mapping_structure = Structure( structure.lattice, species_subset, frac_coords_subset ) else: mapping_structure = structure for symmop in symmetry_operations: new_structure = Structure( mapping_structure.lattice, mapping_structure.species, symmop.operate_multi( mapping_structure.frac_coords ) ) new_mapping = [ x+1 for x in list( coord_list_mapping_pbc( new_structure.frac_coords, mapping_structure.frac_coords ) ) ] if new_mapping not in mappings: mappings.append( new_mapping ) return mappings
def set_miller_family(self): """ get all miller indices for the given maximum index get the list of indices that correspond to the given family of indices """ recp_structure = Structure(self.recp_lattice, ["H"], [[0, 0, 0]]) analyzer = SpacegroupAnalyzer(recp_structure, symprec=0.001) symm_ops = analyzer.get_symmetry_operations() max_index = max(max(m) for m in self.hkl_family) r = list(range(-max_index, max_index + 1)) r.reverse() miller_indices = [] self.all_equiv_millers = [] self.all_surface_energies = [] for miller in itertools.product(r, r, r): if any([i != 0 for i in miller]): d = abs(reduce(gcd, miller)) miller_index = tuple([int(i / d) for i in miller]) for op in symm_ops: for i, u_miller in enumerate(self.hkl_family): if in_coord_list(u_miller, op.operate(miller_index)): self.all_equiv_millers.append(miller_index) self.all_surface_energies.append(self.surface_energies[i])
def set_material_data_from_structure(self, structure, space_group=True, symprec=1e-3, angle_tolerance=5): """ Sets the fields of the Document using a Structure and Spglib to determine the space group properties Args: structure: A |Structure| space_group: if True sets the spacegroup fields using spglib_. symprec (float): Tolerance for symmetry finding. angle_tolerance (float): Angle tolerance for symmetry finding. """ comp = structure.composition el_amt = structure.composition.get_el_amt_dict() self.unit_cell_formula = comp.as_dict() self.reduced_cell_formula = comp.to_reduced_dict self.elements = list(el_amt.keys()) self.nelements = len(el_amt) self.pretty_formula = comp.reduced_formula self.anonymous_formula = comp.anonymized_formula self.nsites = comp.num_atoms self.chemsys = "-".join(sorted(el_amt.keys())) if space_group: sym = SpacegroupAnalyzer(structure, symprec=symprec, angle_tolerance=angle_tolerance) self.spacegroup = SpaceGroupDocument(crystal_system=sym.get_crystal_system(), hall=sym.get_hall(), number=sym.get_space_group_number(), point_group=sym.get_point_group_symbol(), symbol=sym.get_space_group_symbol(), source="spglib")
def symm_reduce(self, coords_set, threshold=1e-6): """ Reduces the set of adsorbate sites by finding removing symmetrically equivalent duplicates Args: coords_set: coordinate set in cartesian coordinates threshold: tolerance for distance equivalence, used as input to in_coord_list_pbc for dupl. checking """ surf_sg = SpacegroupAnalyzer(self.slab, 0.1) symm_ops = surf_sg.get_symmetry_operations() unique_coords = [] # Convert to fractional coords_set = [self.slab.lattice.get_fractional_coords(coords) for coords in coords_set] for coords in coords_set: incoord = False for op in symm_ops: if in_coord_list_pbc(unique_coords, op.operate(coords), atol=threshold): incoord = True break if not incoord: unique_coords += [coords] # convert back to cartesian return [self.slab.lattice.get_cartesian_coords(coords) for coords in unique_coords]
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 cif(src='POSCAR'): """ cifファイルを作成 """ srcpos = Poscar.from_file(src) finder = SpacegroupAnalyzer(srcpos.structure) std_str = finder.get_conventional_standard_structure() cif_obj = CifWriter(std_str, symprec=0.1) cif_obj.write_file('poscar.cif')
def main(): args = parse_command_line_arguments() # initialise poscar = Poscar() # this doesn't really need vasppy. Could just use pymatgen to read the POSCAR # read POSCAR file poscar.read_from( args.poscar ) structure = poscar.to_pymatgen_structure() symmetry_analyzer = SpacegroupAnalyzer( structure, symprec = args.symprec ) print( symmetry_analyzer.get_space_group_symbol() )
def cif(src="POSCAR"): """ cifファイルを作成 """ srcpos = Poscar.from_file(src) finder = SpacegroupAnalyzer(srcpos.structure) std = finder.get_conventional_standard_structure() cif = CifWriter(std, find_spacegroup=True, symprec=0.1) cif.write_file("poscar.cif")
def cif(src): """ cifファイルを作成 """ srcpos = Poscar.from_file(src) finder = SpacegroupAnalyzer(srcpos.structure) std_str = finder.get_conventional_standard_structure() std_cif = CifWriter(std_str, symprec=0.1) std_cif.write_file("poscar.cif")
def prim_cif(self, dst): """ primitive cellでのcifフォーマットをgetする """ finder = SpacegroupAnalyzer(self.structure) structure = finder.get_primitive_standard_structure() structure = finder.get_conventional_standard_structure() cif = CifWriter(structure, symprec=0.1) cif.write_file(dst)
def complete_ordering(self, structure, num_remove_dict): self.logger.debug("Performing complete ordering...") all_structures = [] symprec = 0.2 s = SpacegroupAnalyzer(structure, symprec=symprec) self.logger.debug("Symmetry of structure is determined to be {}." .format(s.get_space_group_symbol())) sg = s.get_space_group_operations() tested_sites = [] starttime = time.time() self.logger.debug("Performing initial ewald sum...") ewaldsum = EwaldSummation(structure) self.logger.debug("Ewald sum took {} seconds." .format(time.time() - starttime)) starttime = time.time() allcombis = [] for ind, num in num_remove_dict.items(): allcombis.append(itertools.combinations(ind, num)) count = 0 for allindices in itertools.product(*allcombis): sites_to_remove = [] indices_list = [] for indices in allindices: sites_to_remove.extend([structure[i] for i in indices]) indices_list.extend(indices) s_new = structure.copy() s_new.remove_sites(indices_list) energy = ewaldsum.compute_partial_energy(indices_list) already_tested = False for i, tsites in enumerate(tested_sites): tenergy = all_structures[i]["energy"] if abs((energy - tenergy) / len(s_new)) < 1e-5 and \ sg.are_symmetrically_equivalent(sites_to_remove, tsites, symm_prec=symprec): already_tested = True if not already_tested: tested_sites.append(sites_to_remove) all_structures.append({"structure": s_new, "energy": energy}) count += 1 if count % 10 == 0: timenow = time.time() self.logger.debug("{} structures, {:.2f} seconds." .format(count, timenow - starttime)) self.logger.debug("Average time per combi = {} seconds" .format((timenow - starttime) / count)) self.logger.debug("{} symmetrically distinct structures found." .format(len(all_structures))) self.logger.debug("Total symmetrically distinct structures found = {}" .format(len(all_structures))) all_structures = sorted(all_structures, key=lambda s: s["energy"]) return all_structures
def analyze_symmetry(args): tolerance = args.symmetry t = [] for filename in args.filenames: s = Structure.from_file(filename, primitive=False) finder = SpacegroupAnalyzer(s, tolerance) dataset = finder.get_symmetry_dataset() t.append([filename, dataset["international"], dataset["number"], dataset["hall"]]) print(tabulate(t, headers=["Filename", "Int Symbol", "Int number", "Hall"]))
def get_ieee_rotation(structure, refine_rotation=True): """ Given a structure associated with a tensor, determines the rotation matrix for IEEE conversion according to the 1987 IEEE standards. Args: structure (Structure): a structure associated with the tensor to be converted to the IEEE standard refine_rotation (bool): whether to refine the rotation using SquareTensor.refine_rotation """ # Check conventional setting: sga = SpacegroupAnalyzer(structure) dataset = sga.get_symmetry_dataset() trans_mat = dataset["transformation_matrix"] conv_latt = Lattice( np.transpose( np.dot(np.transpose(structure.lattice.matrix), np.linalg.inv(trans_mat)))) xtal_sys = sga.get_crystal_system() vecs = conv_latt.matrix lengths = np.array(conv_latt.abc) angles = np.array(conv_latt.angles) rotation = np.zeros((3, 3)) # IEEE rules: a,b,c || x1,x2,x3 if xtal_sys == "cubic": rotation = [vecs[i] / lengths[i] for i in range(3)] # IEEE rules: a=b in length; c,a || x3, x1 elif xtal_sys == "tetragonal": rotation = np.array([ vec / mag for (mag, vec) in sorted(zip(lengths, vecs), key=lambda x: x[0]) ]) if abs(lengths[2] - lengths[1]) < abs(lengths[1] - lengths[0]): rotation[0], rotation[2] = rotation[2], rotation[0].copy() rotation[1] = get_uvec(np.cross(rotation[2], rotation[0])) # IEEE rules: c<a<b; c,a || x3,x1 elif xtal_sys == "orthorhombic": rotation = [vec / mag for (mag, vec) in sorted(zip(lengths, vecs))] rotation = np.roll(rotation, 2, axis=0) # IEEE rules: c,a || x3,x1, c is threefold axis # Note this also includes rhombohedral crystal systems elif xtal_sys in ("trigonal", "hexagonal"): # find threefold axis: tf_index = np.argmin(abs(angles - 120.0)) non_tf_mask = np.logical_not(angles == angles[tf_index]) rotation[2] = get_uvec(vecs[tf_index]) rotation[0] = get_uvec(vecs[non_tf_mask][0]) rotation[1] = get_uvec(np.cross(rotation[2], rotation[0])) # IEEE rules: b,c || x2,x3; alpha=beta=90, c<a elif xtal_sys == "monoclinic": # Find unique axis u_index = np.argmax(abs(angles - 90.0)) n_umask = np.logical_not(angles == angles[u_index]) rotation[1] = get_uvec(vecs[u_index]) # Shorter of remaining lattice vectors for c axis c = [ vec / mag for (mag, vec) in sorted(zip(lengths[n_umask], vecs[n_umask])) ][0] rotation[2] = np.array(c) rotation[0] = np.cross(rotation[1], rotation[2]) # IEEE rules: c || x3, x2 normal to ac plane elif xtal_sys == "triclinic": rotation = [vec / mag for (mag, vec) in sorted(zip(lengths, vecs))] rotation[1] = get_uvec(np.cross(rotation[2], rotation[0])) rotation[0] = np.cross(rotation[1], rotation[2]) rotation = SquareTensor(rotation) if refine_rotation: rotation = rotation.refine_rotation() return rotation
def test_primitive(self): s = Structure.from_spacegroup("Fm-3m", np.eye(3) * 3, ["Cu"], [[0, 0, 0]]) a = SpacegroupAnalyzer(s) self.assertEqual(len(s), 4) self.assertEqual(len(a.find_primitive()), 1)
def test_tricky_structure(self): # for some reason this structure kills spglib1.9 # 1.7 can't find symmetry either, but at least doesn't kill python s = Structure.from_file(test_dir / 'POSCAR.tricky_symmetry') sa = SpacegroupAnalyzer(s, 0.1) sa.get_space_group_symbol() sa.get_space_group_number() sa.get_point_group_symbol() sa.get_crystal_system() sa.get_hall()
def get_shortest_equiv_pairs(self): self._struct.make_supercell([2, 2, 2]) moving_cation = [] #Get symmetric equivalent sites analyzed_struct = SpacegroupAnalyzer(self._struct) analyzed_struct = analyzed_struct.get_symmetrized_structure() equivalent_sites = analyzed_struct.equivalent_sites #Debug #print len(equivalent_sites) for site in self._struct.sites: if site.specie == self._specie: moving_cation.append(site) #Debug #print len(moving_cation) distance = 100 * np.ones( (len(moving_cation), len(moving_cation)), dtype=float) for i in range(len(moving_cation)): for j in range(i): distance[i][j] = moving_cation[i].distance(moving_cation[j]) min_distance = np.amin(distance) min_distance_pairs = [] for i in range(len(moving_cation)): for j in range(i): if distance[i][j] < min_distance * 1.20: min_distance_pairs.append([i, j]) #Debug #print min_distance_pairs #print len(min_distance_pairs) cation_group_num = [] for group in range(len(equivalent_sites)): if equivalent_sites[group][0].specie == self._specie: cation_group_num.append(group) group_pair = defaultdict(lambda: []) for pair in min_distance_pairs: for group in range(len(equivalent_sites)): if moving_cation[pair[0]] in equivalent_sites[group]: group_i = group if moving_cation[pair[1]] in equivalent_sites[group]: group_j = group if group_i == group_j: group_pair[(group_i, group_j)].append( [moving_cation[pair[0]], moving_cation[pair[1]]]) end_point_pair = [] for pairs in group_pair.values(): if pairs: selected_pair = pairs[0] coordin_sum = 10 for pair in pairs: if np.sum(pair[0].frac_coords) + np.sum( pair[1].frac_coords) < coordin_sum: selected_pair = pair coordin_sum = np.sum(pair[0].frac_coords) + np.sum( pair[1].frac_coords) end_point_pair.append(selected_pair) return end_point_pair
def test_is_laue(self): s = Structure.from_spacegroup("Fm-3m", np.eye(3) * 3, ["Cu"], [[0, 0, 0]]) a = SpacegroupAnalyzer(s) self.assertTrue(a.is_laue())
def _generate_transformations( self, structure: Structure) -> Dict[str, MagOrderingTransformation]: """The central problem with trying to enumerate magnetic orderings is that we have to enumerate orderings that might plausibly be magnetic ground states, while not enumerating orderings that are physically implausible. The problem is that it is not always obvious by e.g. symmetry arguments alone which orderings to prefer. Here, we use a variety of strategies (heuristics) to enumerate plausible orderings, and later discard any duplicates that might be found by multiple strategies. This approach is not ideal, but has been found to be relatively robust over a wide range of magnetic structures. Args: structure: A sanitized input structure (_sanitize_input_structure) Returns: A dict of a transformation class instance (values) and name of enumeration strategy (keys) Returns: dict of Transformations keyed by strategy """ formula = structure.composition.reduced_formula transformations: Dict[str, MagOrderingTransformation] = {} # analyzer is used to obtain information on sanitized input analyzer = CollinearMagneticStructureAnalyzer( structure, default_magmoms=self.default_magmoms, overwrite_magmom_mode="replace_all", ) if not analyzer.is_magnetic: raise ValueError( "Not detected as magnetic, add a new default magmom for the " "element you believe may be magnetic?") # now we can begin to generate our magnetic orderings self.logger.info( "Generating magnetic orderings for {}".format(formula)) mag_species_spin = analyzer.magnetic_species_and_magmoms types_mag_species = sorted( analyzer.types_of_magnetic_specie, key=lambda sp: analyzer.default_magmoms.get(str(sp), 0), reverse=True, ) num_mag_sites = analyzer.number_of_magnetic_sites num_unique_sites = analyzer.number_of_unique_magnetic_sites() # enumerations become too slow as number of unique sites (and thus # permutations) increase, 8 is a soft limit, this can be increased # but do so with care if num_unique_sites > self.max_unique_sites: raise ValueError( "Too many magnetic sites to sensibly perform enumeration.") # maximum cell size to consider: as a rule of thumb, if the primitive cell # contains a large number of magnetic sites, perhaps we only need to enumerate # within one cell, whereas on the other extreme if the primitive cell only # contains a single magnetic site, we have to create larger supercells if "max_cell_size" not in self.transformation_kwargs: # TODO: change to 8 / num_mag_sites ? self.transformation_kwargs["max_cell_size"] = max( 1, int(4 / num_mag_sites)) self.logger.info("Max cell size set to {}".format( self.transformation_kwargs["max_cell_size"])) # when enumerating ferrimagnetic structures, it's useful to detect # symmetrically distinct magnetic sites, since different # local environments can result in different magnetic order # (e.g. inverse spinels) # initially, this was done by co-ordination number, but is # now done by a full symmetry analysis sga = SpacegroupAnalyzer(structure) structure_sym = sga.get_symmetrized_structure() wyckoff = ["n/a"] * len(structure) for indices, symbol in zip(structure_sym.equivalent_indices, structure_sym.wyckoff_symbols): for index in indices: wyckoff[index] = symbol is_magnetic_sites = [ True if site.specie in types_mag_species else False for site in structure ] # we're not interested in sites that we don't think are magnetic, # set these symbols to None to filter them out later wyckoff = [ symbol if is_magnetic_site else "n/a" for symbol, is_magnetic_site in zip(wyckoff, is_magnetic_sites) ] structure.add_site_property("wyckoff", wyckoff) wyckoff_symbols = set(wyckoff) - {"n/a"} # if user doesn't specifically request ferrimagnetic orderings, # we apply a heuristic as to whether to attempt them or not if self.automatic: if ("ferrimagnetic_by_motif" not in self.strategies and len(wyckoff_symbols) > 1 and len(types_mag_species) == 1): self.strategies += ["ferrimagnetic_by_motif"] if ("antiferromagnetic_by_motif" not in self.strategies and len(wyckoff_symbols) > 1 and len(types_mag_species) == 1): self.strategies += ["antiferromagnetic_by_motif"] if ("ferrimagnetic_by_species" not in self.strategies and len(types_mag_species) > 1): self.strategies += ["ferrimagnetic_by_species"] # we start with a ferromagnetic ordering if "ferromagnetic" in self.strategies: # TODO: remove 0 spins ! fm_structure = analyzer.get_ferromagnetic_structure() # store magmom as spin property, to be consistent with output from # other transformations fm_structure.add_spin_by_site( fm_structure.site_properties["magmom"]) fm_structure.remove_site_property("magmom") # we now have our first magnetic ordering... self.ordered_structures.append(fm_structure) self.ordered_structure_origins.append("fm") # we store constraint(s) for each strategy first, # and then use each to perform a transformation later all_constraints: Dict[str, Any] = {} # ...to which we can add simple AFM cases first... if "antiferromagnetic" in self.strategies: constraint = MagOrderParameterConstraint( 0.5, # TODO: update MagOrderParameterConstraint in # pymatgen to take types_mag_species directly species_constraints=list(map(str, types_mag_species)), ) all_constraints["afm"] = [constraint] # allows for non-magnetic sublattices if len(types_mag_species) > 1: for sp in types_mag_species: constraints = [ MagOrderParameterConstraint( 0.5, species_constraints=str(sp)) ] all_constraints["afm_by_{}".format(sp)] = constraints # ...and then we also try ferrimagnetic orderings by motif if a # single magnetic species is present... if "ferrimagnetic_by_motif" in self.strategies and len( wyckoff_symbols) > 1: # these orderings are AFM on one local environment, and FM on the rest for symbol in wyckoff_symbols: constraints = [ MagOrderParameterConstraint(0.5, site_constraint_name="wyckoff", site_constraints=symbol), MagOrderParameterConstraint( 1.0, site_constraint_name="wyckoff", site_constraints=list(wyckoff_symbols - {symbol}), ), ] all_constraints["ferri_by_motif_{}".format( symbol)] = constraints # and also try ferrimagnetic when there are multiple magnetic species if "ferrimagnetic_by_species" in self.strategies: sp_list = [str(site.specie) for site in structure] num_sp = {sp: sp_list.count(str(sp)) for sp in types_mag_species} total_mag_sites = sum(num_sp.values()) for sp in types_mag_species: # attempt via a global order parameter all_constraints["ferri_by_{}".format( sp)] = num_sp[sp] / total_mag_sites # attempt via afm on sp, fm on remaining species constraints = [ MagOrderParameterConstraint(0.5, species_constraints=str(sp)), MagOrderParameterConstraint( 1.0, species_constraints=list( map(str, set(types_mag_species) - {sp})), ), ] all_constraints["ferri_by_{}_afm".format(sp)] = constraints # ...and finally, we can try orderings that are AFM on one local # environment, and non-magnetic on the rest -- this is less common # but unless explicitly attempted, these states are unlikely to be found if "antiferromagnetic_by_motif" in self.strategies: for symbol in wyckoff_symbols: constraints = [ MagOrderParameterConstraint(0.5, site_constraint_name="wyckoff", site_constraints=symbol) ] all_constraints["afm_by_motif_{}".format(symbol)] = constraints # and now construct all our transformations for each strategy transformations = {} for name, constraints in all_constraints.items(): trans = MagOrderingTransformation(mag_species_spin, order_parameter=constraints, **self.transformation_kwargs) transformations[name] = trans return transformations
def get_sg_info(ss): finder = SpacegroupAnalyzer(Structure.from_sites(ss), self.symm_prec) sgnum = finder.get_spacegroup_number() return sgnum
def slab(self, miller_index_1, min_slab_size_1=8.0, min_vacuum_size_1=15): from pymatgen.analysis.adsorption import AdsorbateSiteFinder, plot_slab, reorient_z from pymatgen.core.surface import Slab, SlabGenerator, generate_all_slabs, Structure, Lattice, ReconstructionGenerator from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.core.structure import Structure from pymatgen.ext.matproj import MPRester from matplotlib import pyplot as plt from pymatgen.io.vasp.inputs import Poscar from pymatgen.io.vasp.sets import MVLSlabSet import shutil import os mpr = MPRester() #密钥 mp_id = self.mp_id #通过mp_id来索引结构 struct = mpr.get_structure_by_material_id(mp_id) #获取结构信息 struct = SpacegroupAnalyzer( struct).get_conventional_standard_structure() #空间群分析 need_miller_index = miller_index_1 #通过米勒指数,确定要切的晶面 slab = SlabGenerator(struct, miller_index=need_miller_index, min_slab_size=min_slab_size_1,\ min_vacuum_size=min_vacuum_size_1, center_slab=True) #晶面生成器参数 gh = str(miller_index_1).replace(" ", "") for n, slabs in enumerate(slab.get_slabs()): slabs_bak = slabs.copy() #可能的晶面 slabs.make_supercell(self.supercell) #晶胞扩充 cc = slabs.surface_area os.chdir(r"F:\VASP practical\Input") print(os.getcwd()) print(n) A = Poscar(slabs) #将切面转换为Poscar relax = A.structure #将Poscar 转换为结构信息 custom_settings = {"NPAR": 4} # 用户的INCAR 设置 relax = MVLSlabSet(relax, user_incar_settings=custom_settings) #Vasp输入文件生成器 fig = plt.figure() #绘图--确立画布 ax = fig.add_subplot(111) #绘图--确立位置 plot_slab(slabs, ax, adsorption_sites=False) #绘图 dire = str(mp_id) + "---" + str(gh) + '----' + str(n) #设置一个用作存储输入文件的名称 plt.show() # plt.savefig(dire)#将该名称用于保存图片 relax.write_input(str(gh) + '--' + str(n)) #将生成的VASP输入文件写入存储 dire_1 = str(gh) + '--' + str(n) diree = os.chdir("./" + dire_1) fig.savefig('slab.png', bbox_inches='tight', transparent=True, dpi=600, format='png') #定义一个更改当前目录的变量 dire2 = './vaspstd_sub' #确立脚本名称 shutil.copy(r"C:\Users\41958\.spyder-py3\vaspstd_sub", dire2) #将脚本写入VASP输入文件所在文件夹 with open('surface_area', 'w') as f: f.write(str(cc)) print(cc) # os.chdir("../") #将当前目录改为默认目录 os.chdir(r"D:\Desktop\VASP practical\workdir") print(os.getcwd()) print('finished')
def fix_absorbed(self, need_miller_index, mole, num, selective_dynamic, min_slab_size_1=8.0, min_vacuum_size_1=15, judge='fuchdi', appendage=""): from pymatgen import Structure, Lattice, MPRester, Molecule import pymatgen.core.structure import pymatgen.core.sites from pymatgen.analysis.adsorption import AdsorbateSiteFinder, reorient_z, plot_slab from pymatgen.core.surface import generate_all_slabs from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from matplotlib import pyplot as plt from pymatgen.ext.matproj import MPRester from pymatgen.io.vasp.inputs import Poscar from pymatgen.io.vasp.sets import MVLSlabSet from pymatgen.io.cif import CifWriter import os import shutil from openbabel import openbabel from pymatgen.core.surface import Slab, SlabGenerator, generate_all_slabs, Structure, Lattice, ReconstructionGenerator mp_id = self.mp_id os.chdir(r"F:\VASP practical\Input") print(os.getcwd()) # Note that you must provide your own API Key, which can # be accessed via the Dashboard at materialsproject.org mpr = MPRester() struct = mpr.get_structure_by_material_id(mp_id) struct = SpacegroupAnalyzer( struct).get_conventional_standard_structure() # fcc_ni = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3.5), ["Ni", "Ni"], # [[0, 0, 0], [0.5, 0.5, 0.5]]) slab = SlabGenerator(struct, miller_index=need_miller_index, min_slab_size=min_slab_size_1, min_vacuum_size=min_vacuum_size_1, center_slab=True) for n, slabs in enumerate(slab.get_slabs()): if str(n) in str(num): slabs_bak = slabs.copy() #可能的晶面 slabs.make_supercell(self.supercell) print(n) #晶胞扩充 asf_ni_111 = AdsorbateSiteFinder( slabs, selective_dynamics=selective_dynamic) ads_sites = asf_ni_111.find_adsorption_sites() # print(ads_sites) assert len(ads_sites) == 4 fig0 = plt.figure() ax = fig0.add_subplot(111) plot_slab(slabs, ax, adsorption_sites=False) fig1 = plt.figure() ax = fig1.add_subplot(111) os.chdir(r"D:\Desktop\VASP practical\Cif library") print(os.getcwd()) obConversion = openbabel.OBConversion() obConversion.SetInAndOutFormats("pdb", "gjf") mol = openbabel.OBMol() print(mol) c = obConversion.ReadFile(mol, "CH3OH.pdb") obConversion.WriteFile(mol, "CH3OH.pdb" + '1.gjf') adsorbate = Molecule.from_file("CH3OH.pdb" + '.gjf') os.chdir(r"F:\VASP practical\Input") print(os.getcwd()) print(adsorbate.sites) ads_structs = asf_ni_111.add_adsorbate( adsorbate, (20, 20, 20), translate=False, ) # ads_structs = asf_ni_111.generate_adsorption_structures(adsorbate, # repeat=[1, 1, 1]) # A = Poscar(ads_structs[0]) A = Poscar(reorient_z(ads_structs)) #将切面转换为Poscar open('POSCAR', 'w').write(str(A)) p = Poscar.from_file('POSCAR') # w = CifWriter(A.struct) # w.write_file('mystructure.cif') path = r'F:\VASP practical\Input\POSCAR' # 文件路径 if os.path.exists(path): # 如果文件存在 # 删除文件,可使用以下两种方法。 os.remove(path) #os.unlink(path) else: print('no such file:%s' % my_file) # 则返回文件不存在 # w = CifWriter(A.struct) # w.write_file('mystructure.cif') relax = p.structure #将Poscar 转换为结构信息 custom_settings = {"NPAR": 4} # 用户的INCAR 设置 relaxs = MVLSlabSet(relax, user_incar_settings=custom_settings) # Vasp输入文件生成器 dire = str(mp_id) + str(selective_dynamic) + str(mole) + str( need_miller_index).replace(" ", "") + str(n) # print (relax) relaxs.write_input(dire) os.chdir("./" + dire) print(os.getcwd()) fig0.savefig('slab.png', bbox_inches='tight', transparent=True, dpi=600, format='png') plot_slab(ads_structs, ax, adsorption_sites=False, decay=0.09) fig1.savefig('slab_adsobate.png', bbox_inches='tight', transparent=True, dpi=600, format='png') #定义一个更改当前目录的变量 dire2 = './vaspstd_sub' #确立脚本名称 shutil.copy(r"C:\Users\41958\.spyder-py3\vaspstd_sub", dire2) eb = appendage #添加其他INCAR参数 with open('INCAR', 'r') as f1: lines = f1.readlines() with open('INCAR', 'w') as f2: for line in lines: if judge in line: continue f2.write(line) with open('INCAR', 'a') as f3: f3.write(eb) # open('POSCAR001', 'w').write(str(Poscar(reorient_z(ads_structs[0])))) os.chdir(r"D:\Desktop\VASP practical\workdir") print(os.getcwd()) print('finished') # my_lattace = Lattace('mp-698074')#半水石膏 # # my_lattace.phase_out()#生成晶胞优化的输入文件 # go = my_lattace.phase_sol(66,judge='LWAVE', appendage= '\nLWAVE = Ture') # print('yoo')
def get_dos_from_parabolic_bands(st, reclat_matrix, mesh, e_min, e_max, e_points, parabolic_bands, bandgap, width=0.1, SPB_DOS=False, all_values=False): """ Args: st: pmg object of crystal structure to calculate symmetries mesh: list of integers defining the k-mesh on which the dos is required e_min: starting energy (eV) of dos e_max: ending energy (eV) of dos e_points: number of points of the get_dos width: width in eV of the gaussians generated for each energy Returns: e_mesh: energies in eV od the DOS dos: density of states for each energy in e_mesh """ height = 1.0 / (width * np.sqrt(2 * np.pi)) e_mesh, step = np.linspace(e_min, e_max,num=e_points, endpoint=True, retstep=True) e_range = len(e_mesh) ir_kpts_n_weights = SpacegroupAnalyzer(st).get_ir_reciprocal_mesh(mesh) ir_kpts = [k[0] for k in ir_kpts_n_weights] weights = [k[1] for k in ir_kpts_n_weights] ir_kpts = [reclat_matrix.get_cartesian_coords(k)/A_to_nm for k in ir_kpts] w_sum = float(sum(weights)) dos = np.zeros(e_range) if SPB_DOS: volume = st.volume for band in parabolic_bands: for valley in band: offset = valley[-1][0] # each valley has a list of k-points (valley[0]) and [offset, m*] (valley[1]) info m_eff = valley[-1][1] degeneracy = len(valley[0]) for ie, energy in enumerate(e_mesh): dos_temp = volume/(2*pi**2)*(2*m_e*m_eff/hbar**2)**1.5 * 1e-30/e**1.5 if energy <= -bandgap/2.0-offset: # dos_temp *= (-energy-offset)**0.5 dos_temp *= (-energy+bandgap/2.0+offset)**0.5 elif energy >=bandgap/2.0+offset: # dos_temp *= (energy-bandgap-offset)**0.5 dos_temp *= (energy-bandgap/2.0-offset)**0.5 else: dos_temp = 0 dos[ie] += dos_temp * degeneracy else: all_energies = [] all_ks = [] for kpt,w in zip(ir_kpts,weights): for tp in ["n", "p"]: for ib in range(len(parabolic_bands)): if all_values: energy_list, k_dist = get_parabolic_energy(kpt, parabolic_bands, tp, ib=ib, bandgap=bandgap, all_values=all_values) all_energies += energy_list all_ks += [k_dist]*len(energy_list) for energy in energy_list: g = height * np.exp(-((e_mesh - energy) / width) ** 2 / 2.) dos += w/w_sum * g else: energy, v, m_eff = get_parabolic_energy(kpt, parabolic_bands, tp, ib=ib, bandgap=bandgap, all_values=all_values) g = height * np.exp(-((e_mesh - energy) / width) ** 2 / 2.) dos += w/w_sum * g if all_values: scatter(all_ks, all_energies) show() return e_mesh,dos
def calc_shiftk(structure, symprec=0.01, angle_tolerance=5): """ Find the values of ``shiftk`` and ``nshiftk`` appropriated for the sampling of the Brillouin zone. When the primitive vectors of the lattice do NOT form a FCC or a BCC lattice, the usual (shifted) Monkhorst-Pack grids are formed by using nshiftk=1 and shiftk 0.5 0.5 0.5 . This is often the preferred k point sampling. For a non-shifted Monkhorst-Pack grid, use `nshiftk=1` and `shiftk 0.0 0.0 0.0`, but there is little reason to do that. When the primitive vectors of the lattice form a FCC lattice, with rprim:: 0.0 0.5 0.5 0.5 0.0 0.5 0.5 0.5 0.0 the (very efficient) usual Monkhorst-Pack sampling will be generated by using nshiftk= 4 and shiftk:: 0.5 0.5 0.5 0.5 0.0 0.0 0.0 0.5 0.0 0.0 0.0 0.5 When the primitive vectors of the lattice form a BCC lattice, with rprim:: -0.5 0.5 0.5 0.5 -0.5 0.5 0.5 0.5 -0.5 the usual Monkhorst-Pack sampling will be generated by using nshiftk= 2 and shiftk:: 0.25 0.25 0.25 -0.25 -0.25 -0.25 However, the simple sampling nshiftk=1 and shiftk 0.5 0.5 0.5 is excellent. For hexagonal lattices with hexagonal axes, e.g. rprim:: 1.0 0.0 0.0 -0.5 sqrt(3)/2 0.0 0.0 0.0 1.0 one can use nshiftk= 1 and shiftk 0.0 0.0 0.5 In rhombohedral axes, e.g. using angdeg 3*60., this corresponds to shiftk 0.5 0.5 0.5, to keep the shift along the symmetry axis. Returns: Suggested value of shiftk. """ # Find lattice type. from pymatgen.symmetry.analyzer import SpacegroupAnalyzer sym = SpacegroupAnalyzer(structure, symprec=symprec, angle_tolerance=angle_tolerance) lattice_type, spg_symbol = sym.get_lattice_type( ), sym.get_space_group_symbol() # Check if the cell is primitive is_primitive = len(sym.find_primitive()) == len(structure) # Generate the appropriate set of shifts. shiftk = None if is_primitive: if lattice_type == "cubic": if "F" in spg_symbol: # FCC shiftk = [ 0.5, 0.5, 0.5, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5 ] elif "I" in spg_symbol: # BCC shiftk = [0.25, 0.25, 0.25, -0.25, -0.25, -0.25] # shiftk = [0.5, 0.5, 05]) elif lattice_type == "hexagonal": # Find the hexagonal axis and set the shift along it. for i, angle in enumerate(structure.lattice.angles): if abs(angle - 120) < 1.0: j = (i + 1) % 3 k = (i + 2) % 3 hex_ax = [ax for ax in range(3) if ax not in [j, k]][0] break else: raise ValueError("Cannot find hexagonal axis") shiftk = [0.0, 0.0, 0.0] shiftk[hex_ax] = 0.5 elif lattice_type == "tetragonal": if "I" in spg_symbol: # BCT shiftk = [0.25, 0.25, 0.25, -0.25, -0.25, -0.25] if shiftk is None: # Use default value. shiftk = [0.5, 0.5, 0.5] return np.reshape(shiftk, (-1, 3))
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
# Download 3D material data from https://figshare.com/articles/jdft_3d-7-7-2018_json/6815699 # Download 2D material data from https://figshare.com/articles/jdft_2d-7-7-2018_json/6815705 # Curating JARVIS-DFT data d=loadfn('jdft_3d-7-7-2018.json',cls=MontyDecoder) count=0 for i in d: filname=str(i['jid'])+str('.xml') if not os.path.exists(filname) : count=count+1 energy=str(i['fin_en'])+str(',')+str(i['form_enp']) formula=str(i['final_str'].composition.reduced_formula) sgp= str(SpacegroupAnalyzer(i['final_str']).get_spacegroup_symbol()) name=str(i['jid']) print (name) ref=str(i['mpid']) func=str('OptB88vdW') elem='' species=list(set(i['final_str'].species)) for j in species: elem=str(elem)+str(j.symbol)+str('-') encut=str(i['encut']) kpoints=str(i['kpoints'].kpts[0][0])+str('x')+str(i['kpoints'].kpts[0][1])+str('x')+str(i['kpoints'].kpts[0][2]) el_tens=str(i['elastic']) KV=str(i['kv']) GV=str(i['gv']) op_eg=str(i['op_gap']) mbj_eg=str(i['mbj_gap'])
def populate(self, structure, prec=1e-5, maxiter=200, verbose=False, precond=True, vsym=True): """ Takes a partially populated tensor, and populates the non-zero entries according to the following procedure, iterated until the desired convergence (specified via prec) is achieved. 1. Find non-zero entries 2. Symmetrize the tensor with respect to crystal symmetry and (optionally) voigt symmetry 3. Reset the non-zero entries of the original tensor Args: structure (structure object) prec (float): precision for determining a non-zero value maxiter (int): maximum iterations for populating the tensor verbose (bool): whether to populate verbosely precond (bool): whether to precondition by cycling through all symmops and storing new nonzero values, default True vsym (bool): whether to enforce voigt symmetry, defaults to True """ if precond: # Generate the guess from populated sops = SpacegroupAnalyzer(structure).get_symmetry_operations() guess = Tensor(np.zeros(self.shape)) mask = abs(self) > prec guess[mask] = self[mask] def merge(old, new): gmask = np.abs(old) > prec nmask = np.abs(new) > prec new_mask = np.logical_not(gmask) * nmask avg_mask = gmask * nmask old[avg_mask] = (old[avg_mask] + new[avg_mask]) / 2.0 old[new_mask] = new[new_mask] if verbose: print(f"Preconditioning for {len(sops)} symmops") for sop in sops: rot = guess.transform(sop) # Store non-zero entries of new that weren't previously # in the guess in the guess merge(guess, rot) if verbose: print("Preconditioning for voigt symmetry") if vsym: v = guess.voigt perms = list(itertools.permutations(range(len(v.shape)))) for perm in perms: vtrans = np.transpose(v, perm) merge(v, vtrans) guess = Tensor.from_voigt(v) else: guess = np.zeros(self.shape) assert guess.shape == self.shape, "Guess must have same shape" converged = False test_new, test_old = [guess.copy()] * 2 for i in range(maxiter): test_new = test_old.fit_to_structure(structure) if vsym: test_new = test_new.voigt_symmetrized diff = np.abs(test_old - test_new) converged = (diff < prec).all() if converged: break test_new[mask] = self[mask] test_old = test_new if verbose: print(f"Iteration {i}: {np.max(diff)}") if not converged: max_diff = np.max(np.abs(self - test_new)) warnings.warn( f"Warning, populated tensor is not converged with max diff of {max_diff}" ) return self.__class__(test_new)
def is_centro(structure): sga = SpacegroupAnalyzer(structure) return SymmOp.inversion() in sga.get_symmetry_operations()
def sol(self, EB_K_2, miller_index_2, min_slab_size_2=8.0, min_vacuum_size_2=15): from pymatgen.analysis.adsorption import AdsorbateSiteFinder, plot_slab, reorient_z from pymatgen.core.surface import Slab, SlabGenerator, generate_all_slabs, Structure, Lattice, ReconstructionGenerator from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.core.structure import Structure from pymatgen.ext.matproj import MPRester from matplotlib import pyplot as plt from pymatgen.io.vasp.inputs import Poscar from pymatgen.io.vasp.sets import MVLSlabSet import shutil import os mpr = MPRester() #密钥 mp_id = self.mp_id #通过mp_id来索引结构 struct = mpr.get_structure_by_material_id(mp_id) #获取结构信息 struct = SpacegroupAnalyzer( struct).get_conventional_standard_structure() #空间群分析 need_miller_index = miller_index_2 #通过米勒指数,确定要切的晶面 slab = SlabGenerator(struct, miller_index=need_miller_index, min_slab_size=min_slab_size_2,\ min_vacuum_size=min_vacuum_size_2, center_slab=True) #晶面生成器参数 for n, slabs in enumerate(slab.get_slabs()): slabs_bak = slabs.copy() #可能的晶面 slabs.make_supercell(self.supercell) #晶胞扩充 A = Poscar(slabs) #将切面转换为Poscar relax = A.structure #将Poscar 转换为结构信息 custom_settings = {"NPAR": 4} # 用户的INCAR 设置 relax = MVLSlabSet(relax, user_incar_settings=custom_settings) #Vasp输入文件生成器 fig = plt.figure() #绘图--确立画布 ax = fig.add_subplot(111) #绘图--确立位置 plot_slab(slabs, ax, adsorption_sites=False) #绘图 dire = str(mp_id) + "---" + "sol" + str(EB_K_2) + str( need_miller_index) + '----' + str(n) #设置一个用作存储输入文件的名称 plt.savefig(dire) #将该名称用于保存图片 relax.write_input(dire) #将生成的VASP输入文件写入存储 os.chdir("./" + dire) #定义一个更改当前目录的变量 dire2 = './vaspstd_sub' #确立脚本名称 shutil.copy(r"C:\Users\41958\.spyder-py3\vaspstd_sub", dire2) #将脚本写入VASP输入文件所在文件夹 eb = str(EB_K_2) ls = str('TURE') with open('INCAR', 'a') as file_object: file_object.write('LSOL = ' + ls + '\n' + 'EB_K = ' + eb) # os.chdir("../") #将当前目录改为默认目录 os.chdir(r"D:\Desktop\VASP practical\workdir") print(os.getcwd()) print('finished')
def __init__(self, struct, symprec=None): format_str = "{:.8f}" block = OrderedDict() loops = [] spacegroup = ("P 1", 1) if symprec is not None: sf = SpacegroupAnalyzer(struct, symprec) spacegroup = (sf.get_space_group_symbol(), sf.get_space_group_number()) # Needs the refined struture when using symprec. This converts # primitive to conventional structures, the standard for CIF. struct = sf.get_refined_structure() latt = struct.lattice comp = struct.composition no_oxi_comp = comp.element_composition block["_symmetry_space_group_name_H-M"] = spacegroup[0] for cell_attr in ['a', 'b', 'c']: block["_cell_length_" + cell_attr] = format_str.format( getattr(latt, cell_attr)) for cell_attr in ['alpha', 'beta', 'gamma']: block["_cell_angle_" + cell_attr] = format_str.format( getattr(latt, cell_attr)) block["_symmetry_Int_Tables_number"] = spacegroup[1] block["_chemical_formula_structural"] = no_oxi_comp.reduced_formula block["_chemical_formula_sum"] = no_oxi_comp.formula block["_cell_volume"] = latt.volume.__str__() reduced_comp, fu = no_oxi_comp.get_reduced_composition_and_factor() block["_cell_formula_units_Z"] = str(int(fu)) if symprec is None: block["_symmetry_equiv_pos_site_id"] = ["1"] block["_symmetry_equiv_pos_as_xyz"] = ["x, y, z"] else: sf = SpacegroupAnalyzer(struct, symprec) symmops = [] for op in sf.get_symmetry_operations(): v = op.translation_vector symmops.append(SymmOp.from_rotation_and_translation( op.rotation_matrix, v)) ops = [op.as_xyz_string() for op in symmops] block["_symmetry_equiv_pos_site_id"] = \ ["%d" % i for i in range(1, len(ops) + 1)] block["_symmetry_equiv_pos_as_xyz"] = ops loops.append(["_symmetry_equiv_pos_site_id", "_symmetry_equiv_pos_as_xyz"]) contains_oxidation = True try: symbol_to_oxinum = OrderedDict([ (el.__str__(), float(el.oxi_state)) for el in sorted(comp.elements)]) except AttributeError: symbol_to_oxinum = OrderedDict([(el.symbol, 0) for el in sorted(comp.elements)]) contains_oxidation = False if contains_oxidation: block["_atom_type_symbol"] = symbol_to_oxinum.keys() block["_atom_type_oxidation_number"] = symbol_to_oxinum.values() loops.append(["_atom_type_symbol", "_atom_type_oxidation_number"]) atom_site_type_symbol = [] atom_site_symmetry_multiplicity = [] atom_site_fract_x = [] atom_site_fract_y = [] atom_site_fract_z = [] atom_site_label = [] atom_site_occupancy = [] count = 1 if symprec is None: for site in struct: for sp, occu in sorted(site.species_and_occu.items()): atom_site_type_symbol.append(sp.__str__()) atom_site_symmetry_multiplicity.append("1") atom_site_fract_x.append("{0:f}".format(site.a)) atom_site_fract_y.append("{0:f}".format(site.b)) atom_site_fract_z.append("{0:f}".format(site.c)) atom_site_label.append("{}{}".format(sp.symbol, count)) atom_site_occupancy.append(occu.__str__()) count += 1 else: # The following just presents a deterministic ordering. unique_sites = [ (sorted(sites, key=lambda s: tuple([abs(x) for x in s.frac_coords]))[0], len(sites)) for sites in sf.get_symmetrized_structure().equivalent_sites ] for site, mult in sorted( unique_sites, key=lambda t: (t[0].species_and_occu.average_electroneg, -t[1], t[0].a, t[0].b, t[0].c)): for sp, occu in site.species_and_occu.items(): atom_site_type_symbol.append(sp.__str__()) atom_site_symmetry_multiplicity.append("%d" % mult) atom_site_fract_x.append("{0:f}".format(site.a)) atom_site_fract_y.append("{0:f}".format(site.b)) atom_site_fract_z.append("{0:f}".format(site.c)) atom_site_label.append("{}{}".format(sp.symbol, count)) atom_site_occupancy.append(occu.__str__()) count += 1 block["_atom_site_type_symbol"] = atom_site_type_symbol block["_atom_site_label"] = atom_site_label block["_atom_site_symmetry_multiplicity"] = \ atom_site_symmetry_multiplicity block["_atom_site_fract_x"] = atom_site_fract_x block["_atom_site_fract_y"] = atom_site_fract_y block["_atom_site_fract_z"] = atom_site_fract_z block["_atom_site_occupancy"] = atom_site_occupancy loops.append(["_atom_site_type_symbol", "_atom_site_label", "_atom_site_symmetry_multiplicity", "_atom_site_fract_x", "_atom_site_fract_y", "_atom_site_fract_z", "_atom_site_occupancy"]) d = OrderedDict() d[comp.reduced_formula] = CifBlock(block, loops, comp.reduced_formula) self._cf = CifFile(d)
# Note that Composition conveniently allows strings to be treated just # like an Element object. print(comp["Fe"]) print(comp.get_atomic_fraction("Fe")) lattice = mg.Lattice.cubic(4.2) structure = mg.Structure(lattice, ["Cs", "Cl"], [[0, 0, 0], [0.5, 0.5, 0.5]]) print(structure.volume) print(structure[0]) # You can create a Structure using spacegroup symmetry as well. li2o = mg.Structure.from_spacegroup("Fm-3m", mg.Lattice.cubic(3), ["Li", "O"], [[0.25, 0.25, 0.25], [0, 0, 0]]) # Integrated symmetry analysis tools from spglib. from pymatgen.symmetry.analyzer import SpacegroupAnalyzer finder = SpacegroupAnalyzer(structure) # print(finder.get_spacegroup_symbol()) # Convenient IO to various formats. You can specify various formats. # Without a filename, a string is returned. Otherwise, # the output is written to the file. If only the filenmae is provided, # the format is intelligently determined from a file. # structure.to(fmt="poscar") # structure.to(filename="POSCAR") # structure.to(filename="CsCl.cif") # Pythonic API for editing Structures and Molecules (v2.9.1 onwards) # Changing the specie of a site. structure[1] = "F" print(structure)
import argparse import pickle # analyze space group of POSCAR using pymatgen from pymatgen.symmetry.analyzer import SpacegroupAnalyzer if __name__ == '__main__': # ---------- argparse parser = argparse.ArgumentParser() parser.add_argument('-a', '--all_id', help='flag for all structures', action='store_true') parser.add_argument('-i', '--index', help='structure ID', type=int, nargs='*') parser.add_argument('-s', '--symmetrized', help='flag for symmetrized structure', action='store_true') parser.add_argument('infile', help='input file') args = parser.parse_args() # ---------- load struc_data with open(args.infile, 'rb') as f: struc_data = pickle.load(f) # ---------- write POSCAR if args.index: for cid in args.index: # print(struc_data[cid].to(fmt='poscar')) print(struc_data[cid]) # print(SpacegroupAnalyzer(struc_data[cid], symprec=.1).get_space_group_symbol()) # print(SpacegroupAnalyzer(struc_data[cid], 0.1).get_space_group_symbol()) print(SpacegroupAnalyzer(struc_data[cid], 0.1).get_space_group_number()) spg_no = SpacegroupAnalyzer(struc_data[cid], 0.1).get_space_group_number() # struc_data[cid].to(fmt='cif', filename='{}.cif'.format(cid)) struc_data[cid].to(fmt='POSCAR', filename='{}.vasp'.format(str(cid)+'_'+str(spg_no))) raise SystemExit()
from pyxtal.lattice import cellsize for G in range(1, 231): #for G in [90, 99, 105, 107, 203, 210, 224, 226, 227, 228]: g = Group(G) subs = g.get_max_t_subgroup() indices = subs['index'] hs = subs['subgroup'] relations = subs['relations'] tran = subs['transformation'] letter = str(g[0].multiplicity) + g[0].letter print(G) C1 = random_crystal(G, ['C'], [int(g[0].multiplicity / cellsize(g))], sites=[[letter]]) pmg_s1 = C1.to_pymatgen() sga1 = SpacegroupAnalyzer(pmg_s1).get_space_group_symbol() # each subgroup for i in range(len(relations)): C2 = C1.subgroup(eps=0, idx=[i], once=True) pmg_s2 = C2.to_pymatgen() try: sga2 = SpacegroupAnalyzer(pmg_s2, symprec=1e-4).get_space_group_symbol() except: #print("unable to find the space group") sga2 = None print(G, hs[i], g.symbol, sga1, Group(hs[i]).symbol, sga2, i) if not sm.StructureMatcher().fit(pmg_s1, pmg_s2): print('WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW') print(C1) print(C2)
def _gen_input_file(self): """ Generate the necessary struct_enum.in file for enumlib. See enumlib documentation for details. """ coord_format = "{:.6f} {:.6f} {:.6f}" # Using symmetry finder, get the symmetrically distinct sites. fitter = SpacegroupAnalyzer(self.structure, self.symm_prec) symmetrized_structure = fitter.get_symmetrized_structure() logger.debug("Spacegroup {} ({}) with {} distinct sites".format( fitter.get_spacegroup_symbol(), fitter.get_spacegroup_number(), len(symmetrized_structure.equivalent_sites))) """ Enumlib doesn"t work when the number of species get too large. To simplify matters, we generate the input file only with disordered sites and exclude the ordered sites from the enumeration. The fact that different disordered sites with the exact same species may belong to different equivalent sites is dealt with by having determined the spacegroup earlier and labelling the species differently. """ # index_species and index_amounts store mappings between the indices # used in the enum input file, and the actual species and amounts. index_species = [] index_amounts = [] #Stores the ordered sites, which are not enumerated. ordered_sites = [] disordered_sites = [] coord_str = [] for sites in symmetrized_structure.equivalent_sites: if sites[0].is_ordered: ordered_sites.append(sites) else: sp_label = [] species = {k: v for k, v in sites[0].species_and_occu.items()} if sum(species.values()) < 1 - EnumlibAdaptor.amount_tol: #Let us first make add a dummy element for every single #site whose total occupancies don't sum to 1. species[DummySpecie("X")] = 1 - sum(species.values()) for sp in species.keys(): if sp not in index_species: index_species.append(sp) sp_label.append(len(index_species) - 1) index_amounts.append(species[sp] * len(sites)) else: ind = index_species.index(sp) sp_label.append(ind) index_amounts[ind] += species[sp] * len(sites) sp_label = "/".join(["{}".format(i) for i in sorted(sp_label)]) for site in sites: coord_str.append("{} {}".format( coord_format.format(*site.coords), sp_label)) disordered_sites.append(sites) def get_sg_info(ss): finder = SpacegroupAnalyzer(Structure.from_sites(ss), self.symm_prec) sgnum = finder.get_spacegroup_number() return sgnum curr_sites = list(itertools.chain.from_iterable(disordered_sites)) min_sgnum = get_sg_info(curr_sites) logger.debug("Disorderd sites has sgnum %d" % (min_sgnum)) #It could be that some of the ordered sites has a lower symmetry than #the disordered sites. So we consider the lowest symmetry sites as #disordered in our enumeration. self.ordered_sites = [] to_add = [] if self.check_ordered_symmetry: for sites in ordered_sites: temp_sites = list(curr_sites) + sites sgnum = get_sg_info(temp_sites) if sgnum < min_sgnum: logger.debug("Adding {} to sites to be ordered. " "New sgnum {}".format(sites, sgnum)) to_add = sites min_sgnum = sgnum for sites in ordered_sites: if sites == to_add: index_species.append(sites[0].specie) index_amounts.append(len(sites)) sp_label = len(index_species) - 1 logger.debug( "Lowest symmetry {} sites are included in enum.".format( sites[0].specie)) for site in sites: coord_str.append("{} {}".format( coord_format.format(*site.coords), sp_label)) disordered_sites.append(sites) else: self.ordered_sites.extend(sites) self.index_species = index_species lattice = self.structure.lattice output = [self.structure.formula, "bulk"] for vec in lattice.matrix: output.append(coord_format.format(*vec)) output.append("{}".format(len(index_species))) output.append("{}".format(len(coord_str))) output.extend(coord_str) output.append("{} {}".format(self.min_cell_size, self.max_cell_size)) output.append(str(self.enum_precision_parameter)) output.append("partial") ndisordered = sum([len(s) for s in disordered_sites]) base = ndisordered #10 ** int(math.ceil(math.log10(ndisordered))) #To get a reasonable number of structures, we fix concentrations to the #range expected in the original structure. total_amounts = sum(index_amounts) for amt in index_amounts: conc = amt / total_amounts if abs(conc * base - round(conc * base)) < 1e-5: output.append("{} {} {}".format(int(round(conc * base)), int(round(conc * base)), base)) else: min_conc = int(math.floor(conc * base)) output.append("{} {} {}".format(min_conc - 1, min_conc + 1, base)) output.append("") logger.debug("Generated input file:\n{}".format("\n".join(output))) with open("struct_enum.in", "w") as f: f.write("\n".join(output))
def get_pattern(self, structure, scaled=True, two_theta_range=(0, 90)): """ Calculates the powder neutron diffraction pattern for a structure. Args: structure (Structure): Input structure scaled (bool): Whether to return scaled intensities. The maximum peak is set to a value of 100. Defaults to True. Use False if you need the absolute values to combine ND plots. two_theta_range ([float of length 2]): Tuple for range of two_thetas to calculate in degrees. Defaults to (0, 90). Set to None if you want all diffracted beams within the limiting sphere of radius 2 / wavelength. Returns: (NDPattern) """ if self.symprec: finder = SpacegroupAnalyzer(structure, symprec=self.symprec) structure = finder.get_refined_structure() wavelength = self.wavelength latt = structure.lattice is_hex = latt.is_hexagonal() # Obtained from Bragg condition. Note that reciprocal lattice # vector length is 1 / d_hkl. min_r, max_r = (0, 2 / wavelength) if two_theta_range is None else \ [2 * sin(radians(t / 2)) / wavelength for t in two_theta_range] # Obtain crystallographic reciprocal lattice points within range recip_latt = latt.reciprocal_lattice_crystallographic recip_pts = recip_latt.get_points_in_sphere( [[0, 0, 0]], [0, 0, 0], max_r) if min_r: recip_pts = [pt for pt in recip_pts if pt[1] >= min_r] # Create a flattened array of coeffs, fcoords and occus. This is # used to perform vectorized computation of atomic scattering factors # later. Note that these are not necessarily the same size as the # structure as each partially occupied specie occupies its own # position in the flattened array. coeffs = [] fcoords = [] occus = [] dwfactors = [] for site in structure: for sp, occu in site.species.items(): try: c = ATOMIC_SCATTERING_LEN[sp.symbol] except KeyError: raise ValueError("Unable to calculate ND pattern as " "there is no scattering coefficients for" " %s." % sp.symbol) coeffs.append(c) dwfactors.append(self.debye_waller_factors.get(sp.symbol, 0)) fcoords.append(site.frac_coords) occus.append(occu) coeffs = np.array(coeffs) fcoords = np.array(fcoords) occus = np.array(occus) dwfactors = np.array(dwfactors) peaks = {} two_thetas = [] for hkl, g_hkl, ind, _ in sorted( recip_pts, key=lambda i: (i[1], -i[0][0], -i[0][1], -i[0][2])): # Force miller indices to be integers. hkl = [int(round(i)) for i in hkl] if g_hkl != 0: d_hkl = 1 / g_hkl # Bragg condition theta = asin(wavelength * g_hkl / 2) # s = sin(theta) / wavelength = 1 / 2d = |ghkl| / 2 (d = # 1/|ghkl|) s = g_hkl / 2 # Calculate Debye-Waller factor dw_correction = np.exp(-dwfactors * (s**2)) # Vectorized computation of g.r for all fractional coords and # hkl. g_dot_r = np.dot(fcoords, np.transpose([hkl])).T[0] # Structure factor = sum of atomic scattering factors (with # position factor exp(2j * pi * g.r and occupancies). # Vectorized computation. f_hkl = np.sum(coeffs * occus * np.exp(2j * pi * g_dot_r) * dw_correction) # Lorentz polarization correction for hkl lorentz_factor = 1 / (sin(theta) ** 2 * cos(theta)) # Intensity for hkl is modulus square of structure factor. i_hkl = (f_hkl * f_hkl.conjugate()).real two_theta = degrees(2 * theta) if is_hex: # Use Miller-Bravais indices for hexagonal lattices. hkl = (hkl[0], hkl[1], - hkl[0] - hkl[1], hkl[2]) # Deal with floating point precision issues. ind = np.where(np.abs(np.subtract(two_thetas, two_theta)) < self.TWO_THETA_TOL) if len(ind[0]) > 0: peaks[two_thetas[ind[0][0]]][0] += i_hkl * lorentz_factor peaks[two_thetas[ind[0][0]]][1].append(tuple(hkl)) else: peaks[two_theta] = [i_hkl * lorentz_factor, [tuple(hkl)], d_hkl] two_thetas.append(two_theta) # Scale intensities so that the max intensity is 100. max_intensity = max([v[0] for v in peaks.values()]) x = [] y = [] hkls = [] d_hkls = [] for k in sorted(peaks.keys()): v = peaks[k] fam = get_unique_families(v[1]) if v[0] / max_intensity * 100 > self.SCALED_INTENSITY_TOL: x.append(k) y.append(v[0]) hkls.append(fam) d_hkls.append(v[2]) nd = DiffractionPattern(x, y, hkls, d_hkls) if scaled: nd.normalize(mode="max", value=100) return nd
def get_end_point_pairs(self): """ will change self._struct into supercell structure if necessary the distinct sites in list should be less than 4. :return: a list of end points pairs for diffusion """ self._struct.make_supercell([2, 2, 2]) moving_cation = [] #Get symmetric equivalent sites analyzed_struct = SpacegroupAnalyzer(self._struct) analyzed_struct = analyzed_struct.get_symmetrized_structure() equivalent_sites = analyzed_struct.equivalent_sites #Debug #print len(equivalent_sites) for site in self._struct.sites: if site.specie == self._specie: moving_cation.append(site) #Debug #print len(moving_cation) distance = 100 * np.ones( (len(moving_cation), len(moving_cation)), dtype=float) for i in range(len(moving_cation)): for j in range(i): distance[i][j] = moving_cation[i].distance(moving_cation[j]) min_distance = np.amin(distance) min_distance_pairs = [] for i in range(len(moving_cation)): for j in range(i): if distance[i][j] < min_distance * 1.15: min_distance_pairs.append([i, j]) #Debug #print min_distance_pairs #print len(min_distance_pairs) cation_group_num = [] for group in range(len(equivalent_sites)): if equivalent_sites[group][0].specie == self._specie: cation_group_num.append(group) group_pair = {} for i in range(len(cation_group_num)): for j in range(i + 1): group_pair[(cation_group_num[i], cation_group_num[j])] = [] for pair in min_distance_pairs: for group in range(len(equivalent_sites)): if moving_cation[pair[0]] in equivalent_sites[group]: group_i = group if moving_cation[pair[1]] in equivalent_sites[group]: group_j = group (group_i, group_j) = EndPointFinder._reorder(group_i, group_j) if moving_cation[pair[0]] in equivalent_sites[group_i]: group_pair[(group_i, group_j)].append( [moving_cation[pair[0]], moving_cation[pair[1]]]) else: group_pair[(group_i, group_j)].append( [moving_cation[pair[1]], moving_cation[pair[0]]]) end_point_pair = [] for pairs in group_pair.values(): if pairs: selected_pair = pairs[0] coordin_sum = 10 for pair in pairs: if np.sum(pair[0].frac_coords) + np.sum( pair[1].frac_coords) < coordin_sum: selected_pair = pair coordin_sum = np.sum(pair[0].frac_coords) + np.sum( pair[1].frac_coords) end_point_pair.append(selected_pair) return end_point_pair
def generate_doc(self, dir_name, vasprun_files, outcar_files): """ Adapted from matgendb.creator.generate_doc """ try: # basic properties, incl. calcs_reversed and run_stats fullpath = os.path.abspath(dir_name) d = {k: v for k, v in self.additional_fields.items()} d["schema"] = {"code": "atomate", "version": VaspDrone.__version__} d["dir_name"] = fullpath d["calcs_reversed"] = [self.process_vasprun(dir_name, taskname, filename) for taskname, filename in vasprun_files.items()] outcar_data = [Outcar(os.path.join(dir_name, filename)).as_dict() for taskname, filename in outcar_files.items()] run_stats = {} for i, d_calc in enumerate(d["calcs_reversed"]): run_stats[d_calc["task"]["name"]] = outcar_data[i].pop("run_stats") if d_calc.get("output"): d_calc["output"].update({"outcar": outcar_data[i]}) else: d_calc["output"] = {"outcar": outcar_data[i]} try: overall_run_stats = {} for key in ["Total CPU time used (sec)", "User time (sec)", "System time (sec)", "Elapsed time (sec)"]: overall_run_stats[key] = sum([v[key] for v in run_stats.values()]) run_stats["overall"] = overall_run_stats except: logger.error("Bad run stats for {}.".format(fullpath)) d["run_stats"] = run_stats # reverse the calculations data order so newest calc is first d["calcs_reversed"].reverse() # set root formula/composition keys based on initial and final calcs d_calc_init = d["calcs_reversed"][-1] d_calc_final = d["calcs_reversed"][0] d["chemsys"] = "-".join(sorted(d_calc_final["elements"])) comp = Composition(d_calc_final["composition_unit_cell"]) d["formula_anonymous"] = comp.anonymized_formula d["formula_reduced_abc"] = comp.reduced_composition.alphabetical_formula for root_key in ["completed_at", "nsites", "composition_unit_cell", "composition_reduced", "formula_pretty", "elements", "nelements"]: d[root_key] = d_calc_final[root_key] # store the input key based on initial calc # store any overrides to the exchange correlation functional xc = d_calc_init["input"]["incar"].get("GGA") if xc: xc = xc.upper() p = d_calc_init["input"]["potcar_type"][0].split("_") pot_type = p[0] functional = "lda" if len(pot_type) == 1 else "_".join(p[1:]) d["input"] = {"structure": d_calc_init["input"]["structure"], "is_hubbard": d_calc_init.pop("is_hubbard"), "hubbards": d_calc_init.pop("hubbards"), "is_lasph": d_calc_init["input"]["incar"].get("LASPH", False), "potcar_spec": d_calc_init["input"].get("potcar_spec"), "xc_override": xc, "pseudo_potential": {"functional": functional.lower(), "pot_type": pot_type.lower(), "labels": d_calc_init["input"]["potcar"]}, "parameters": d_calc_init["input"]["parameters"], "incar": d_calc_init["input"]["incar"] } # store the output key based on final calc d["output"] = { "structure": d_calc_final["output"]["structure"], "density": d_calc_final.pop("density"), "energy": d_calc_final["output"]["energy"], "energy_per_atom": d_calc_final["output"]["energy_per_atom"]} # patch calculated magnetic moments into final structure if len(d_calc_final["output"]["outcar"]["magnetization"]) != 0: magmoms = [m["tot"] for m in d_calc_final["output"]["outcar"]["magnetization"]] s = Structure.from_dict(d["output"]["structure"]) s.add_site_property('magmom', magmoms) d["output"]["structure"] = s.as_dict() calc = d["calcs_reversed"][0] try: d["output"].update({"bandgap": calc["output"]["bandgap"], "cbm": calc["output"]["cbm"], "vbm": calc["output"]["vbm"], "is_gap_direct": calc["output"]["is_gap_direct"], "is_metal": calc["output"]["is_metal"]}) except Exception: if self.bandstructure_mode is True: import traceback logger.error(traceback.format_exc()) logger.error("Error in " + os.path.abspath(dir_name) + ".\n" + traceback.format_exc()) raise sg = SpacegroupAnalyzer(Structure.from_dict(d_calc_final["output"]["structure"]), 0.1) if not sg.get_symmetry_dataset(): sg = SpacegroupAnalyzer(Structure.from_dict(d_calc_final["output"]["structure"]), 1e-3, 1) d["output"]["spacegroup"] = { "source": "spglib", "symbol": sg.get_space_group_symbol(), "number": sg.get_space_group_number(), "point_group": sg.get_point_group_symbol(), "crystal_system": sg.get_crystal_system(), "hall": sg.get_hall()} if d["input"]["parameters"].get("LEPSILON"): for k in ['epsilon_static', 'epsilon_static_wolfe', 'epsilon_ionic']: d["output"][k] = d_calc_final["output"][k] if SymmOp.inversion() not in sg.get_symmetry_operations(): for k in ["piezo_ionic_tensor", "piezo_tensor"]: d["output"][k] = d_calc_final["output"]["outcar"][k] d["state"] = "successful" if d_calc["has_vasp_completed"] else "unsuccessful" self.set_analysis(d) d["last_updated"] = datetime.datetime.today() return d except Exception: import traceback logger.error(traceback.format_exc()) logger.error("Error in " + os.path.abspath(dir_name) + ".\n" + traceback.format_exc()) raise
def test_get_kpoint_weights(self): for name in ["SrTiO3", "LiFePO4", "Graphite"]: s = PymatgenTest.get_structure(name) a = SpacegroupAnalyzer(s) ir_mesh = a.get_ir_reciprocal_mesh((4, 4, 4)) weights = [i[1] for i in ir_mesh] weights = np.array(weights) / sum(weights) for i, w in zip(weights, a.get_kpoint_weights([i[0] for i in ir_mesh])): self.assertAlmostEqual(i, w) for name in ["SrTiO3", "LiFePO4", "Graphite"]: s = PymatgenTest.get_structure(name) a = SpacegroupAnalyzer(s) ir_mesh = a.get_ir_reciprocal_mesh((1, 2, 3)) weights = [i[1] for i in ir_mesh] weights = np.array(weights) / sum(weights) for i, w in zip(weights, a.get_kpoint_weights([i[0] for i in ir_mesh])): self.assertAlmostEqual(i, w) v = Vasprun(test_dir / "vasprun.xml") a = SpacegroupAnalyzer(v.final_structure) wts = a.get_kpoint_weights(v.actual_kpoints) for w1, w2 in zip(v.actual_kpoints_weights, wts): self.assertAlmostEqual(w1, w2) kpts = [[0, 0, 0], [0.15, 0.15, 0.15], [0.2, 0.2, 0.2]] self.assertRaises(ValueError, a.get_kpoint_weights, kpts)
def get_plot_2d_concise(self, structure: Structure) -> go.Figure: """ Generates the concise 2D diffraction pattern of the input structure of a smaller size and without layout. Does not display. Args: structure (Structure): The input structure. Returns: Figure """ if self.symprec: finder = SpacegroupAnalyzer(structure, symprec=self.symprec) structure = finder.get_refined_structure() points = self.generate_points(-10, 11) tem_dots = self.tem_dots(structure, points) xs = [] ys = [] hkls = [] intensities = [] for dot in tem_dots: if dot.hkl != (0, 0, 0): xs.append(dot.position[0]) ys.append(dot.position[1]) hkls.append(dot.hkl) intensities.append(dot.intensity) data = [ go.Scatter(x=xs, y=ys, text=hkls, mode='markers', hoverinfo='skip', marker=dict(size=4, cmax=1, cmin=0, color=intensities, colorscale=[[0, 'black'], [1.0, 'white']]), showlegend=False) ] layout = go.Layout( xaxis=dict(autorange=True, showgrid=False, zeroline=False, showline=False, ticks='', showticklabels=False), yaxis=dict( autorange=True, showgrid=False, zeroline=False, showline=False, ticks='', showticklabels=False, ), plot_bgcolor='black', margin={ 'l': 0, 'r': 0, 't': 0, 'b': 0 }, width=121, height=121, ) fig = go.Figure(data=data, layout=layout) fig.layout.update(showlegend=False) return fig
def setUp(self): p = Poscar.from_file(str(test_dir / 'POSCAR')) self.structure = p.structure self.sg1 = SpacegroupAnalyzer(self.structure, 0.001).get_space_group_operations()
def get_plot_2d(self, structure: Structure) -> go.Figure: """ Generates the 2D diffraction pattern of the input structure. Args: structure (Structure): The input structure. Returns: Figure """ if self.symprec: finder = SpacegroupAnalyzer(structure, symprec=self.symprec) structure = finder.get_refined_structure() points = self.generate_points(-10, 11) tem_dots = self.tem_dots(structure, points) xs = [] ys = [] hkls = [] intensities = [] for dot in tem_dots: xs.append(dot.position[0]) ys.append(dot.position[1]) hkls.append(str(dot.hkl)) intensities.append(dot.intensity) hkls = list( map(unicodeify_spacegroup, list(map(latexify_spacegroup, hkls)))) data = [ go.Scatter( x=xs, y=ys, text=hkls, hoverinfo='text', mode='markers', marker=dict(size=8, cmax=1, cmin=0, color=intensities, colorscale=[[0, 'black'], [1.0, 'white']]), showlegend=False, ), go.Scatter( x=[0], y=[0], text="(0, 0, 0): Direct beam", hoverinfo='text', mode='markers', marker=dict(size=14, cmax=1, cmin=0, color='white'), showlegend=False, ) ] layout = go.Layout( title='2D Diffraction Pattern<br>Beam Direction: ' + ''.join(str(e) for e in self.beam_direction), font=dict(size=14, color='#7f7f7f'), hovermode='closest', xaxis=dict(range=[-5.5, 5.5], showgrid=False, zeroline=False, showline=False, ticks='', showticklabels=False), yaxis=dict( range=[-5.5, 5.5], showgrid=False, zeroline=False, showline=False, ticks='', showticklabels=False, ), width=550, height=550, paper_bgcolor='rgba(100,110,110,0.5)', plot_bgcolor='black', ) fig = go.Figure(data=data, layout=layout) return fig
def EV_find(self): hit = [] count = [] phases = [] volumes = [] ITEMS = [] for i in self.items: try: mm = i['metadata']['tag'] except: continue if mm in hit: volume = i['output']['structure']['lattice']['volume'] if volume not in volumes[hit.index(mm)]: volumes[hit.index(mm)].append(volume) count[hit.index(mm)] += 1 #if mm=='5252ccc3-e8da-499f-bb9e-9cf7eb1c5370': print("eeeeeeeee",mm, pot) else: ITEMS.append(i) hit.append(mm) count.append(1) volumes.append([i['output']['structure']['lattice']['volume']]) pot = i['input']['pseudo_potential']['functional'].upper() #if mm=='5252ccc3-e8da-499f-bb9e-9cf7eb1c5370': print("eeeeeeeee",mm, pot) if pot == "": pot = i['orig_inputs']['potcar']['functional'].upper() if pot == 'Perdew-Zunger81'.upper(): pot = "LDA" try: pot += "+" + i['input']['GGA'] except: pass if i['input']['is_hubbard']: pot += '+U' try: if i['input']['incar']['LSORBIT']: potsoc = pot + "SOC" except: potsoc = pot structure = Structure.from_dict(i['output']['structure']) natoms = len(structure.sites) formula_pretty = structure.composition.reduced_formula try: formula2composition(formula_pretty) except: formula_pretty = reduced_formula( structure.composition.alphabetical_formula) sa = SpacegroupAnalyzer(structure) phasename = formula_pretty+'_'\ + sa.get_space_group_symbol().replace('/','.')+'_'+str(sa.get_space_group_number())+potsoc if phasename in phases: for jj in range(10000): nphasename = phasename + "#" + str(jj) if nphasename in phases: continue phasename = nphasename break phases.append(phasename) for i, m in enumerate(hit): if count[i] < self.nV: continue if self.skipby(phases[i]): continue sys.stdout.write('{}, static: {:>2}, {}\n'.format( m, count[i], phases[i])) EV, POSCAR, INCAR = get_rec_from_metatag(self.vasp_db, m) evdir = './E-V/' if not os.path.exists(evdir): os.mkdir(evdir) folder = evdir + phases[i] if not os.path.exists(folder): os.mkdir(folder) with open(folder + '/POSCAR', 'w') as fp: fp.write(POSCAR) readme = {} readme['E-V'] = EV readme['INCAR'] = INCAR readme['POSCAR'] = POSCAR with open(folder + '/readme', 'w') as fp: myjsonout(readme, fp, indent="", comma="") thermoplot(folder, "0 K total energies (eV/atom)", EV['volumes'], EV['energies'])
def from_structure(cls, structure, equivalent_wyckoff_sites=None): """ Parameters ---------- structure : pymatgen.Structure equivalent_wyckoff_sites : list of lists List of Wyckoff sites that are treated as the same sublattice, e.g. [['b', 'f']] will give combine Wyckoff site 'b' and Wyckoff site 'f' into one sublattice. Putting the same Wyckoff site in multiple equivalent groups will produce undefined results. Returns ------- PRLStructure """ struct = PRLStructure.from_dict(structure.as_dict()) # normalize the input structure to a pure element to get Wyckoff sites structure = Structure.from_dict(structure.as_dict()) structure.replace_species({sp.name: "H" for sp in structure.species}) sga = SpacegroupAnalyzer(structure) wyckoff_sites = sga.get_symmetry_dataset()['wyckoffs'] true_sublattices = sorted(set(wyckoff_sites)) if equivalent_wyckoff_sites is not None: # transform the true sublattices by combining equivalent sites combined_sublattices = [ ''.join(sorted(sites)) for sites in equivalent_wyckoff_sites ] def match_subl(candidate): for subl in combined_sublattices: # if the candidate site is in the combined sublattice, return the combined sublattice if candidate in subl: return subl # no match found return candidate new_subl_model = sorted( set([match_subl(subl) for subl in true_sublattices])) else: new_subl_model = true_sublattices #ratios = [sum([1 if site in subl else 0 for site in wyckoff_sites]) for subl in new_subl_model] config = [] occ = [] ratios = [] for subl in new_subl_model: species_frequency_dict = {} for site, wyckoff_site in zip(struct.sites, wyckoff_sites): if wyckoff_site in subl: species = site.specie.name species_frequency_dict[ species] = species_frequency_dict.get(species, 0) + 1 total_subl_occupation = sum(species_frequency_dict.values()) subl_species = sorted(set(species_frequency_dict.keys())) subl_occpancy = [ species_frequency_dict[sp] / total_subl_occupation for sp in subl_species ] config.append(subl_species) occ.append(subl_occpancy) ratios.append(total_subl_occupation) #config = [sorted(set([site.specie.name for site, wyckoff in if wyckoff in subl])) for subl in new_subl_model] struct.sublattice_configuration = config struct.sublattice_occupancies = occ struct.sublattice_site_ratios = ratios return struct