def add_snl(self, snl, force_new=False, snlgroup_guess=None): try: self.lock_db() snl_id = self._get_next_snl_id() spstruc = snl.structure.copy() spstruc.remove_oxidation_states() sf = SpacegroupAnalyzer(spstruc, SPACEGROUP_TOLERANCE) sf.get_space_group_operations() sgnum = sf.get_space_group_number() if sf.get_space_group_number() \ else -1 sgsym = sf.get_space_group_symbol() if sf.get_space_group_symbol() \ else 'unknown' sghall = sf.get_hall() if sf.get_hall() else 'unknown' sgxtal = sf.get_crystal_system() if sf.get_crystal_system() \ else 'unknown' sglatt = sf.get_lattice_type() if sf.get_lattice_type( ) else 'unknown' sgpoint = sf.get_point_group_symbol() mpsnl = MPStructureNL.from_snl(snl, snl_id, sgnum, sgsym, sghall, sgxtal, sglatt, sgpoint) snlgroup, add_new, spec_group = self.add_mpsnl( mpsnl, force_new, snlgroup_guess) self.release_lock() return mpsnl, snlgroup.snlgroup_id, spec_group except: self.release_lock() traceback.print_exc() raise ValueError("Error while adding SNL!")
def add_snl(self, snl, force_new=False, snlgroup_guess=None): try: self.lock_db() snl_id = self._get_next_snl_id() spstruc = snl.structure.copy() spstruc.remove_oxidation_states() sf = SpacegroupAnalyzer(spstruc, SPACEGROUP_TOLERANCE) sf.get_space_group_operations() sgnum = sf.get_space_group_number() if sf.get_space_group_number() \ else -1 sgsym = sf.get_space_group_symbol() if sf.get_space_group_symbol() \ else 'unknown' sghall = sf.get_hall() if sf.get_hall() else 'unknown' sgxtal = sf.get_crystal_system() if sf.get_crystal_system() \ else 'unknown' sglatt = sf.get_lattice_type() if sf.get_lattice_type() else 'unknown' sgpoint = sf.get_point_group_symbol() mpsnl = MPStructureNL.from_snl(snl, snl_id, sgnum, sgsym, sghall, sgxtal, sglatt, sgpoint) snlgroup, add_new, spec_group = self.add_mpsnl(mpsnl, force_new, snlgroup_guess) self.release_lock() return mpsnl, snlgroup.snlgroup_id, spec_group except: self.release_lock() traceback.print_exc() raise ValueError("Error while adding SNL!")
def test_sites(self): struc = molecular_crystal(36, ["H2O"], [2]) pmg_struc = struc.to_pymatgen() sga = SpacegroupAnalyzer(pmg_struc) self.assertTrue(sga.get_space_group_symbol() == "Cmc2_1") struc = molecular_crystal(36, ["H2O"], [4], sites=[["4a", "4a"]]) pmg_struc = struc.to_pymatgen() sga = SpacegroupAnalyzer(pmg_struc) self.assertTrue(sga.get_space_group_symbol() == "Cmc2_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 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(os.path.join(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 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(os.path.join(PymatgenTest.TEST_FILES_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 test_magnetic(self): lfp = PymatgenTest.get_structure("LiFePO4") sg = SpacegroupAnalyzer(lfp, 0.1) self.assertEqual(sg.get_space_group_symbol(), "Pnma") magmoms = [0] * len(lfp) magmoms[4] = 1 magmoms[5] = -1 magmoms[6] = 1 magmoms[7] = -1 lfp.add_site_property("magmom", magmoms) sg = SpacegroupAnalyzer(lfp, 0.1) self.assertEqual(sg.get_space_group_symbol(), "Pnma")
def test_sites(self): struc = pyxtal(molecular=True) struc.from_random(3, 19, ["H2O"], [4]) pmg_struc = struc.to_pymatgen() sga = SpacegroupAnalyzer(pmg_struc) self.assertTrue(sga.get_space_group_symbol() == "P2_12_12_1") struc = pyxtal(molecular=True) struc.from_random(3, 36, ["H2O"], [8], sites=[["4a", "4a"]]) pmg_struc = struc.to_pymatgen() sga = SpacegroupAnalyzer(pmg_struc) self.assertTrue(sga.get_space_group_symbol() == "Cmc2_1")
def get_endpoints_structure_from_yaml(self, layer, yaml_file, store_path): finder = SpacegroupAnalyzer(self.init_struc) stream = open(yaml_file, 'r') doc = yaml.load(stream) tag = 0 endpoints_structures = {} for key, value in doc.items(): crystal_lattice = key content = value eg_struc = mpr.get_structure_by_material_id(content['example']) finder_eg = SpacegroupAnalyzer(eg_struc) if (finder.get_space_group_symbol() == finder_eg.get_space_group_symbol()): tag = 1 break if (tag == 0): logging.error("Sorry, we only support the structure of FCC, BCC \ and HCP right now.") return None elif (tag == 1): slip_system = content['slip system'] for slip_system_key, slip_system_value in slip_system.items(): surface = slip_system_key if isinstance(slip_system_value, list): logging.info("One surface corresponding to more than one \ Burgers vector.") for direction in slip_system_value: burger_vector = direction logging.info("surface: {}, burger_vector: {}".format( surface, burger_vector)) data_dict = self.get_endpoints_structure( store_path, layer, surface, burger_vector, crystal_lattice) endpoints_structures[ "%s_%s" % (surface, burger_vector)] = data_dict["Structure"] POSACR_path = data_dict["POSACR_path"] logging.info( "The new poscar file is: {}".format(POSACR_path)) else: burger_vector = slip_system_value logging.info("surface: {}, burger_vector: {}".format( surface, burger_vector)) data_dict = self.get_endpoints_structure( store_path, layer, surface, burger_vector, crystal_lattice) endpoints_structures[ "%s_%s" % (surface, burger_vector)] = data_dict["Structure"] POSACR_path = data_dict["POSACR_path"] logging.info( "The new poscar file is: {}".format(POSACR_path)) return (endpoints_structures)
def test_magnetic(self): lfp = PymatgenTest.get_structure("LiFePO4") sg = SpacegroupAnalyzer(lfp, 0.1) self.assertEqual(sg.get_space_group_symbol(), "Pnma") magmoms = [0] * len(lfp) magmoms[4] = 1 magmoms[5] = -1 magmoms[6] = 1 magmoms[7] = -1 lfp.add_site_property("magmom", magmoms) sg = SpacegroupAnalyzer(lfp, 0.1) self.assertEqual(sg.get_space_group_symbol(), "Pnma")
def from_structure(cls, structure: Structure) -> "SymmetryData": symprec = SETTINGS.SYMPREC sg = SpacegroupAnalyzer(structure, symprec=symprec) symmetry: Dict[str, Any] = {"symprec": symprec} if not sg.get_symmetry_dataset(): sg = SpacegroupAnalyzer(structure, 1e-3, 1) symmetry["symprec"] = 1e-3 symmetry.update({ "source": "spglib", "symbol": sg.get_space_group_symbol(), "number": sg.get_space_group_number(), "point_group": sg.get_point_group_symbol(), "crystal_system": CrystalSystem(sg.get_crystal_system().title()), "hall": sg.get_hall(), "version": spglib.__version__, }) return SymmetryData(**symmetry)
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 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 __init__(self, band: BandStructureSymmLine, band2: BandStructureSymmLine = None, absolute: bool = False, y_range: list = None, legend: bool = False, symprec: float = SYMMETRY_TOLERANCE, angle_tolerance: float = ANGLE_TOL): bs_plotter = ModBSPlotter(band) composition = str(band.structure.composition) sga = SpacegroupAnalyzer(structure=band.structure, symprec=symprec, angle_tolerance=angle_tolerance) sg_symbol = sga.get_space_group_symbol() sg_num = sga.get_space_group_number() kwargs = { "ylim": y_range, "legend": legend, "zero_to_efermi": absolute, "title": f"{composition} SG: {sg_symbol} ({sg_num})" } if not band2: self.plotter = bs_plotter.get_plot(**kwargs) else: bs_plotter2 = ModBSPlotter(band2) self.plotter = bs_plotter2.plot_compare(bs_plotter, **kwargs)
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 get_structure_data(structure): sga = SpacegroupAnalyzer(structure) return { "xtal_system": sga.get_crystal_system(), "spacegroup_sym": sga.get_space_group_symbol(), "spacegroup_num": sga.get_space_group_number() }
def _log_structure_information(structure: Structure, symprec): log_banner("STRUCTURE") logger.info("Structure information:") comp = structure.composition lattice = structure.lattice formula = comp.get_reduced_formula_and_factor(iupac_ordering=True)[0] if not symprec: symprec = 1e-32 sga = SpacegroupAnalyzer(structure, symprec=symprec) spg = unicodeify_spacegroup(sga.get_space_group_symbol()) comp_info = [ "formula: {}".format(unicodeify(formula)), "# sites: {}".format(structure.num_sites), "space group: {}".format(spg), ] log_list(comp_info) logger.info("Lattice:") lattice_info = [ "a, b, c [Å]: {:.2f}, {:.2f}, {:.2f}".format(*lattice.abc), "α, β, γ [°]: {:.0f}, {:.0f}, {:.0f}".format(*lattice.angles), ] log_list(lattice_info)
def __init__(self, group_uuid, structure, sc_size, process_label, energy_threshold=0.01, symprec=1e-3, if_with_energy=True): """ Parameters: ----------- uuid : int A uuid of a given group to generate the cluster structure : Structure A structure object to generate the symmetry operations sc_size : int A list of supercell size to generate symmetry operations process_label : str workchain name to query calculation from energy_threshold : float Tolerance of energy difference threshold. Default = 0.01 symprec : float Tolerance in atomic distance to test if atoms are symmetrically similar. Default = 0.1 (if for positions obtain from electronic structure) if_with_energy : bool If False, to not query energy . Default=True """ self.group_uuid = group_uuid self.structure = structure self.process_label = process_label self.energy_threshold = energy_threshold self.symprec = symprec self.if_with_energy = if_with_energy #self.frac_coords = frac_coords if sc_size is None or sc_size == "": self.sc_size = "1 1 1" else: self.sc_size = sc_size self.structure_sc = structure.copy() self.structure_sc.make_supercell( [int(x) for x in self.sc_size.split()]) # Query nodes, energy, positions QG = QueryCalculationsFromGroup(self.group_uuid, self.process_label) self.uuid = QG.get_relaxed_nodes() self.positions = QG.get_positions() if self.if_with_energy: self.energies = QG.get_energies() else: self.energies = np.zeros([len(self.uuid)]) """#To find the space group symmetry operations of the structure """ SA = SpacegroupAnalyzer(self.structure_sc) self.SG = SpacegroupOperations( SA.get_space_group_number(), SA.get_space_group_symbol(), SA.get_symmetry_operations(cartesian=False))
def print_info(struc): """ Read a Structure object and print structure information. Args: struc : pymatgen Structure object. Returns: None. """ print(struc) print('\n') sga = SpacegroupAnalyzer(struc) print('The space group: {} {}\n'.format(sga.get_space_group_number(), sga.get_space_group_symbol())) equi_sites = sga.get_symmetrized_structure().equivalent_sites for sites in equi_sites: print('the symmtry equivalent sites are \n{}\n'.format(sites)) sym_operations = sga.get_symmetry_operations() print('there are {} symmetry operations: \n {} \n'.format( len(sym_operations), sym_operations))
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 poscar(self): """ generating poscar for relaxation calculations """ print( '--------------------------------------------------------------------------------------------------------' ) print("Generation of VASP files for the cell relaxation:") print( '--------------------------------------------------------------------------------------------------------' ) self.symData.structure = Structure.from_file(self.args.pos[0]) sym1 = float(self.args.sympre[0]) sym2 = float(self.args.symang[0]) aa = SpacegroupAnalyzer(self.symData.structure, symprec=sym1, angle_tolerance=sym2) self.symData.space_group = aa.get_space_group_number() print("Space group number =", self.symData.space_group) spg = aa.get_space_group_symbol() print("Space group symbol =", str(spg)) self.symData.number_of_species = len(self.symData.structure.species) print("Number of atoms = {}".format(len( self.symData.structure.species))) pos_name = "POSCAR" structure00 = Poscar(self.symData.structure) structure00.write_file(filename=pos_name, significant_figures=16) return self.symData
def set_material_data_from_structure(self, structure, space_group=True, symprec=1e-3, angle_tolerance=5): 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 set_output_data(self, d_calc, d): """ set the 'output' key """ d["output"] = { "structure": d_calc["output"]["structure"], "density": d_calc.pop("density"), "energy": d_calc["output"]["energy"], "energy_per_atom": d_calc["output"]["energy_per_atom"] } d["output"].update(self.get_basic_processed_data(d)) sg = SpacegroupAnalyzer( Structure.from_dict(d_calc["output"]["structure"]), 0.1) if not sg.get_symmetry_dataset(): sg = SpacegroupAnalyzer( Structure.from_dict(d_calc["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["output"][k]
def run_task(self, fw_spec): additional_fields = self.get("additional_fields", {}) # pass the additional_fields first to avoid overriding BoltztrapAnalyzer items d = additional_fields.copy() btrap_dir = os.path.join(os.getcwd(), "boltztrap") d["boltztrap_dir"] = btrap_dir bta = BoltztrapAnalyzer.from_files(btrap_dir) d.update(bta.as_dict()) d["scissor"] = bta.intrans["scissor"] # trim the output for x in ['cond', 'seebeck', 'kappa', 'hall', 'mu_steps', 'mu_doping', 'carrier_conc']: del d[x] if not self.get("hall_doping"): del d["hall_doping"] bandstructure_dir = os.getcwd() d["bandstructure_dir"] = bandstructure_dir # add the structure v, o = get_vasprun_outcar(bandstructure_dir, parse_eigen=False, parse_dos=False) structure = v.final_structure d["structure"] = structure.as_dict() d["formula_pretty"] = structure.composition.reduced_formula d.update(get_meta_from_structure(structure)) # add the spacegroup sg = SpacegroupAnalyzer(Structure.from_dict(d["structure"]), 0.1) d["spacegroup"] = {"symbol": sg.get_space_group_symbol(), "number": sg.get_space_group_number(), "point_group": sg.get_point_group_symbol(), "source": "spglib", "crystal_system": sg.get_crystal_system(), "hall": sg.get_hall()} d["created_at"] = datetime.utcnow() db_file = env_chk(self.get('db_file'), fw_spec) if not db_file: del d["dos"] with open(os.path.join(btrap_dir, "boltztrap.json"), "w") as f: f.write(json.dumps(d, default=DATETIME_HANDLER)) else: mmdb = VaspCalcDb.from_db_file(db_file, admin=True) # dos gets inserted into GridFS dos = json.dumps(d["dos"], cls=MontyEncoder) fsid, compression = mmdb.insert_gridfs(dos, collection="dos_boltztrap_fs", compress=True) d["dos_boltztrap_fs_id"] = fsid del d["dos"] mmdb.db.boltztrap.insert(d)
def run_task(self, fw_spec): additional_fields = self.get("additional_fields", {}) # pass the additional_fields first to avoid overriding BoltztrapAnalyzer items d = additional_fields.copy() btrap_dir = os.path.join(os.getcwd(), "boltztrap") d["boltztrap_dir"] = btrap_dir bta = BoltztrapAnalyzer.from_files(btrap_dir) d.update(bta.as_dict()) d["scissor"] = bta.intrans["scissor"] # trim the output for x in ['cond', 'seebeck', 'kappa', 'hall', 'mu_steps', 'mu_doping', 'carrier_conc']: del d[x] if not self.get("hall_doping"): del d["hall_doping"] bandstructure_dir = os.getcwd() d["bandstructure_dir"] = bandstructure_dir # add the structure v, o = get_vasprun_outcar(bandstructure_dir, parse_eigen=False, parse_dos=False) structure = v.final_structure d["structure"] = structure.as_dict() d["formula_pretty"] = structure.composition.reduced_formula d.update(get_meta_from_structure(structure)) # add the spacegroup sg = SpacegroupAnalyzer(Structure.from_dict(d["structure"]), 0.1) d["spacegroup"] = {"symbol": sg.get_space_group_symbol(), "number": sg.get_space_group_number(), "point_group": sg.get_point_group_symbol(), "source": "spglib", "crystal_system": sg.get_crystal_system(), "hall": sg.get_hall()} d["created_at"] = datetime.utcnow() db_file = env_chk(self.get('db_file'), fw_spec) if not db_file: del d["dos"] with open(os.path.join(btrap_dir, "boltztrap.json"), "w") as f: f.write(json.dumps(d, default=DATETIME_HANDLER)) else: mmdb = VaspCalcDb.from_db_file(db_file, admin=True) # dos gets inserted into GridFS dos = json.dumps(d["dos"], cls=MontyEncoder) fsid, compression = mmdb.insert_gridfs(dos, collection="dos_boltztrap_fs", compress=True) d["dos_boltztrap_fs_id"] = fsid del d["dos"] mmdb.db.boltztrap.insert(d)
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 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 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 test_read(self): # test reading structure from external struc = pyxtal(molecular=True) struc.from_seed(seed=cif_path + "aspirin.cif", molecule="aspirin") pmg_struc = struc.to_pymatgen() sga = SpacegroupAnalyzer(pmg_struc) self.assertTrue(sga.get_space_group_symbol() == "P2_1/c") C = struc.subgroup_once(eps=0, H=4) pmg_s2 = C.to_pymatgen() self.assertTrue(sm.StructureMatcher().fit(pmg_struc, pmg_s2))
def print_spg(src='POSCAR'): """ space group を return """ srcpos = Poscar.from_file(src) finder = SpacegroupAnalyzer(srcpos.structure, symprec=5e-2, angle_tolerance=8) spg = finder.get_space_group_symbol() spg_num = finder.get_space_group_number() return spg, spg_num
def output_struc(entry, eng, tol): id = entry.entry_id todo = mpr.get_structure_by_material_id(id) finder = SpacegroupAnalyzer(todo, symprec=tol, angle_tolerance=5) newStruc = finder.get_refined_structure() if len(newStruc.frac_coords) > 16: newStruc = newStruc.get_primitive_structure() p1 = ['{:6.3f}'.format(j) for j in newStruc.lattice.abc] p2 = ['{:6.2f}'.format(j) for j in newStruc.lattice.angles] sym = finder.get_space_group_symbol() s = ' '.join(map(str, p1 + p2)) print('%-16s %40s %6.3f %10s [%s]' % (id, s, eng, sym, newStruc.formula)) return newStruc
def space_group_symbol_from_structure( structure ): """ Returns the symbol for the space group defined by this structure. Args: structure (pymatgen ``Structure``): The input structure. Returns: (str): The space group symbol. """ symmetry_analyzer = SpacegroupAnalyzer( structure ) symbol = symmetry_analyzer.get_space_group_symbol() return symbol
def space_group_symbol_from_structure(structure): """ Returns the symbol for the space group defined by this structure. Args: structure (pymatgen ``Structure``): The input structure. Returns: (str): The space group symbol. """ symmetry_analyzer = SpacegroupAnalyzer(structure) symbol = symmetry_analyzer.get_space_group_symbol() return symbol
def get_structure_tag(self): "Structure and composition related tags " analyzer = SpacegroupAnalyzer(self.structure, symprec=0.1) self.spacegroup = analyzer.get_space_group_symbol() # get equivalent sites of the current structure in a list of dict self.equivSiteList = get_equiv_site_list(self.structure) (self.dOO_min, self.dOO_min_indices) = \ cluster.get_min_OO_dist(self.structure) self.volume = self.structure.lattice.volume / self.nb_cell
def debye_find(self): hit = [] phases = [] count = [] for i in self.items: mm = i['metadata'] if mm in hit: count[hit.index(mm)] += 1 else: hit.append(mm) count.append(1) structure = Structure.from_dict(i['structure']) 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()) if phasename in phases: for jj in range(10000): nphasename = phasename + "#" + str(jj) if nphasename in phases: continue phasename = nphasename break phases.append(phasename) print("\nfound complete calculations in the collection:", self.qhamode, "\n") all_static_calculations = list((self.vasp_db).db['tasks'].\ find({'$and':[{'metadata': { "$exists": True }}, {'adopted': True} ]},\ {'metadata':1, 'output':1, 'input':1, 'orig_inputs':1})) for i, m in enumerate(hit): if self.skipby(phases[i], m['tag']): continue static_calculations = [ f for f in all_static_calculations if f['metadata']['tag'] == m['tag'] ] for ii, calc in enumerate(static_calculations): potsoc = get_used_pot(calc) if self.qhamode == 'qha': potsoc += "_debye" pname = phases[i].split('#') if len(pname) > 1: phases[i] = pname[0] + potsoc + '#' + pname[1] else: phases[i] = pname[0] + potsoc break print(m, ":", phases[i]) self.tags.append({'tag': m['tag'], 'phasename': phases[i]})
def get_spacegroup(strt=""): """ Get spacegroup of a Structure pbject Args: strt: Structure object Returns: num: spacegroup number symb: spacegroup symbol """ finder = SpacegroupAnalyzer(strt) num = finder.get_space_group_number() symb = finder.get_space_group_symbol() return num, symb
def unique_symmetry_operations_as_vectors_from_structure( structure, verbose=False, subset=None, atol=1e-5 ): """ 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. atol (Optional [float]): tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each symmetry operation. Returns: (list[list]): a list of lists, containing the symmetry operations as vector mappings. .. _pymatgen: http://pymatgen.org .. _coordinate mapping: http://pymatgen.org/pymatgen.util.coord_utils.html#pymatgen.util.coord_utils.coord_list_mapping_pbc """ if isinstance( structure, Structure ): instantiate_structure = partial( Structure, lattice=structure.lattice, coords_are_cartesian=True ) coord_mapping = structure_cartesian_coordinates_mapping mapping_list = structure_mapping_list symmetry_analyzer = SpacegroupAnalyzer( structure ) if verbose: print( "The space group for this structure is {}".format( symmetry_analyzer.get_space_group_symbol()) ) elif isinstance( structure, Molecule ): instantiate_structure = Molecule coord_mapping = molecule_cartesian_coordinates_mapping mapping_list = molecule_mapping_list symmetry_analyzer = PointGroupAnalyzer( structure, tolerance=atol ) if verbose: print( "The point group for this structure is {}".format( symmetry_analyzer.get_pointgroup()) ) else: raise ValueError( 'structure argument should be a Structure or Molecule object' ) symmetry_operations = symmetry_analyzer.get_symmetry_operations() mappings = [] if subset: species_subset = [ spec for i,spec in enumerate( structure.species ) if i in subset ] cart_coords_subset = [ coord for i, coord in enumerate( structure.cart_coords ) if i in subset ] mapping_structure = instantiate_structure( species=species_subset, coords=cart_coords_subset ) else: mapping_structure = structure for symmop in symmetry_operations: cart_coords = coord_mapping( mapping_structure, symmop ) new_structure = instantiate_structure( species=mapping_structure.species, coords=cart_coords ) new_mapping = [ x+1 for x in list( mapping_list( new_structure, mapping_structure, atol ) ) ] if new_mapping not in mappings: mappings.append( new_mapping ) return mappings
def print_spg(src='POSCAR'): """ 空間群を出力 """ srcpos = Poscar.from_file(src) # srcpos.structure | fnc.echo finder = SpacegroupAnalyzer(srcpos.structure, symprec=5e-2, angle_tolerance=8) # dir(finder) | fnc.echo # srcpos | fnc.echo # help(finder.get_space_group_symbol) # finder._space_group_data | fnc.echo spg = finder.get_space_group_symbol() spg_num = finder.get_space_group_number() print(spg) print(spg_num)
def analyze_symmetry(request): results = {} symprec = float(request.POST["symprec"]) angle_tolerance = float(request.POST["angle_tolerance"]) for name, f in request.FILES.items(): name, s = get_structure(f) a = SpacegroupAnalyzer(s, symprec=symprec, angle_tolerance=angle_tolerance) d = {} d["international"] = a.get_space_group_symbol() d["number"] = a.get_space_group_number() d["hall"] = a.get_hall() d["point_group"] = a.get_point_group() d["crystal_system"] = a.get_crystal_system() results[name] = d return results
def get_highsymweight(filename): Mg2Si = pmg.Structure.from_file(filename) finder = SpacegroupAnalyzer(Mg2Si) symbol = finder.get_space_group_symbol() HKpath = HighSymmKpath(Mg2Si) Keys = list() Coords = list() for key in HKpath.kpath['kpoints']: Keys.append(key) Coords.append(HKpath.kpath['kpoints'][key]) count = 0 Keylist = list() Coordslist = list() for i in np.arange(len(Keys) - 1): if (count-1)%3 == 0: #count-1 can be intergely divided by 3 Keylist.append(Keys[0]) Coordslist.append(Coords[0]) count+=1 Keylist.append(Keys[i+1]) Coordslist.append(Coords[i+1]) count+=1 if (count-1)%3 == 0: Keylist.append(Keys[0]) Coordslist.append(Coords[0]) Kweight = np.zeros(len(Keys) - 1) kmesh = finder.get_ir_reciprocal_mesh(mesh=(50,50,50)) for i in np.arange(len(Keys) - 1): (zerocount,nonzeroratio) = Coordcharacter(Coords[i+1]) for j in np.arange(len(kmesh)): (mzerocount,mnonzeroratio) = Coordcharacter(kmesh[j][0]) if len(mnonzeroratio) == len(nonzeroratio): remainlogic = np.abs(nonzeroratio - mnonzeroratio) < 0.01 # 0.01 is a value can get enough accurate results if zerocount == mzerocount and remainlogic.all(): if kmesh[j][1] > Kweight[i]: Kweight[i] = kmesh[j][1] else: pass return Keylist, Coordslist, Kweight
def debye_find(self): hit = [] phases = [] count = [] for i in self.items: """ try: ii = len(i['debye']) mm = i['metadata'] except: continue if ii < 6: continue """ mm = i['metadata'] if mm in hit: count[hit.index(mm)] += 1 else: hit.append(mm) count.append(1) structure = Structure.from_dict(i['structure']) 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()) if phasename in phases: for jj in range(10000): nphasename = phasename + "#" + str(jj) if nphasename in phases: continue phasename = nphasename break phases.append(phasename) print("\nfound complete calculations in the collection:", self.qhamode, "\n") for i, m in enumerate(hit): if self.skipby(phases[i], m['tag']): continue print(m, ":", phases[i]) self.tags.append({'tag': m['tag'], 'phasename': phases[i]})
def test_single_specie(self): # print("test_h2o") struc = molecular_crystal(36, ["H2O"], [4], sites=[["8b"]]) struc.to_file() self.assertTrue(struc.valid) # test space group pmg_struc = struc.to_pymatgen() sga = SpacegroupAnalyzer(pmg_struc) # print(sga.get_space_group_symbol()) self.assertTrue(sga.get_space_group_number() >= 36) # print(pmg_struc.frac_coords[:3]) # test rotation ax = struc.mol_sites[0].orientation.axis struc.mol_sites[0].rotate(axis=[1, 0, 0], angle=90) pmg_struc = struc.to_pymatgen() sga = SpacegroupAnalyzer(pmg_struc) pmg_struc.to("cif", "1.cif") self.assertTrue(sga.get_space_group_symbol() == "Cmc2_1")
def set_output_data(self, d_calc, d): """ set the 'output' key """ d["output"] = { "structure": d_calc["output"]["structure"], "density": d_calc.pop("density"), "energy": d_calc["output"]["energy"], "energy_per_atom": d_calc["output"]["energy_per_atom"]} d["output"].update(self.get_basic_processed_data(d)) sg = SpacegroupAnalyzer(Structure.from_dict(d_calc["output"]["structure"]), 0.1) if not sg.get_symmetry_dataset(): sg = SpacegroupAnalyzer(Structure.from_dict(d_calc["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["output"][k]
class SpacegroupAnalyzerTest(PymatgenTest): def setUp(self): p = Poscar.from_file(os.path.join(test_dir, 'POSCAR')) self.structure = p.structure self.sg = SpacegroupAnalyzer(self.structure, 0.001) self.disordered_structure = self.get_structure('Li10GeP2S12') self.disordered_sg = SpacegroupAnalyzer(self.disordered_structure, 0.001) s = p.structure.copy() site = s[0] del s[0] s.append(site.species_and_occu, site.frac_coords) self.sg3 = SpacegroupAnalyzer(s, 0.001) graphite = self.get_structure('Graphite') graphite.add_site_property("magmom", [0.1] * len(graphite)) self.sg4 = SpacegroupAnalyzer(graphite, 0.001) self.structure4 = graphite 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_magnetic(self): lfp = PymatgenTest.get_structure("LiFePO4") sg = SpacegroupAnalyzer(lfp, 0.1) self.assertEqual(sg.get_space_group_symbol(), "Pnma") magmoms = [0] * len(lfp) magmoms[4] = 1 magmoms[5] = -1 magmoms[6] = 1 magmoms[7] = -1 lfp.add_site_property("magmom", magmoms) sg = SpacegroupAnalyzer(lfp, 0.1) self.assertEqual(sg.get_space_group_symbol(), "Pnma") def test_get_space_symbol(self): self.assertEqual(self.sg.get_space_group_symbol(), "Pnma") self.assertEqual(self.disordered_sg.get_space_group_symbol(), "P4_2/nmc") self.assertEqual(self.sg3.get_space_group_symbol(), "Pnma") self.assertEqual(self.sg4.get_space_group_symbol(), "P6_3/mmc") def test_get_space_number(self): self.assertEqual(self.sg.get_space_group_number(), 62) self.assertEqual(self.disordered_sg.get_space_group_number(), 137) self.assertEqual(self.sg4.get_space_group_number(), 194) def test_get_hall(self): self.assertEqual(self.sg.get_hall(), '-P 2ac 2n') self.assertEqual(self.disordered_sg.get_hall(), 'P 4n 2n -1n') def test_get_pointgroup(self): self.assertEqual(self.sg.get_point_group_symbol(), 'mmm') self.assertEqual(self.disordered_sg.get_point_group_symbol(), '4/mmm') def test_get_symmetry_dataset(self): ds = self.sg.get_symmetry_dataset() self.assertEqual(ds['international'], 'Pnma') def test_get_crystal_system(self): crystal_system = self.sg.get_crystal_system() self.assertEqual('orthorhombic', crystal_system) self.assertEqual('tetragonal', self.disordered_sg.get_crystal_system()) def test_get_symmetry_operations(self): for sg, structure in [(self.sg, self.structure), (self.sg4, self.structure4)]: pgops = sg.get_point_group_operations() fracsymmops = sg.get_symmetry_operations() symmops = sg.get_symmetry_operations(True) latt = structure.lattice for fop, op, pgop in zip(fracsymmops, symmops, pgops): # translation vector values should all be 0 or 0.5 t = fop.translation_vector * 2 self.assertArrayAlmostEqual(t - np.round(t), 0) self.assertArrayAlmostEqual(fop.rotation_matrix, pgop.rotation_matrix) for site in structure: newfrac = fop.operate(site.frac_coords) newcart = op.operate(site.coords) self.assertTrue(np.allclose(latt.get_fractional_coords(newcart), newfrac)) found = False newsite = PeriodicSite(site.species_and_occu, newcart, latt, coords_are_cartesian=True) for testsite in structure: if newsite.is_periodic_image(testsite, 1e-3): found = True break self.assertTrue(found) # Make sure this works for any position, not just the atomic # ones. random_fcoord = np.random.uniform(size=(3)) random_ccoord = latt.get_cartesian_coords(random_fcoord) newfrac = fop.operate(random_fcoord) newcart = op.operate(random_ccoord) self.assertTrue(np.allclose(latt.get_fractional_coords(newcart), newfrac)) def test_get_refined_structure(self): for a in self.sg.get_refined_structure().lattice.angles: self.assertEqual(a, 90) refined = self.disordered_sg.get_refined_structure() for a in refined.lattice.angles: self.assertEqual(a, 90) self.assertEqual(refined.lattice.a, refined.lattice.b) s = self.get_structure('Li2O') sg = SpacegroupAnalyzer(s, 0.001) self.assertEqual(sg.get_refined_structure().num_sites, 4 * s.num_sites) def test_get_symmetrized_structure(self): symm_struct = self.sg.get_symmetrized_structure() for a in symm_struct.lattice.angles: self.assertEqual(a, 90) self.assertEqual(len(symm_struct.equivalent_sites), 5) symm_struct = self.disordered_sg.get_symmetrized_structure() self.assertEqual(len(symm_struct.equivalent_sites), 8) self.assertEqual([len(i) for i in symm_struct.equivalent_sites], [16,4,8,4,2,8,8,8]) s1 = symm_struct.equivalent_sites[1][1] s2 = symm_struct[symm_struct.equivalent_indices[1][1]] self.assertEqual(s1, s2) self.assertEqual(self.sg4.get_symmetrized_structure()[0].magmom, 0.1) self.assertEqual(symm_struct.wyckoff_symbols[0], '16h') # self.assertEqual(symm_struct[0].wyckoff, "16h") def test_find_primitive(self): """ F m -3 m Li2O testing of converting to primitive cell """ parser = CifParser(os.path.join(test_dir, 'Li2O.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure) primitive_structure = s.find_primitive() self.assertEqual(primitive_structure.formula, "Li2 O1") # This isn't what is expected. All the angles should be 60 self.assertAlmostEqual(primitive_structure.lattice.alpha, 60) self.assertAlmostEqual(primitive_structure.lattice.beta, 60) self.assertAlmostEqual(primitive_structure.lattice.gamma, 60) self.assertAlmostEqual(primitive_structure.lattice.volume, structure.lattice.volume / 4.0) def test_get_ir_reciprocal_mesh(self): grid = self.sg.get_ir_reciprocal_mesh() self.assertEqual(len(grid), 216) self.assertAlmostEqual(grid[1][0][0], 0.1) self.assertAlmostEqual(grid[1][0][1], 0.0) self.assertAlmostEqual(grid[1][0][2], 0.0) self.assertAlmostEqual(grid[1][1], 2) def test_get_conventional_standard_structure(self): parser = CifParser(os.path.join(test_dir, 'bcc_1927.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 9.1980270633769461) self.assertAlmostEqual(conv.lattice.b, 9.1980270633769461) self.assertAlmostEqual(conv.lattice.c, 9.1980270633769461) parser = CifParser(os.path.join(test_dir, 'btet_1915.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 5.0615106678044235) self.assertAlmostEqual(conv.lattice.b, 5.0615106678044235) self.assertAlmostEqual(conv.lattice.c, 4.2327080177761687) parser = CifParser(os.path.join(test_dir, 'orci_1010.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 2.9542233922299999) self.assertAlmostEqual(conv.lattice.b, 4.6330325651443296) self.assertAlmostEqual(conv.lattice.c, 5.373703587040775) parser = CifParser(os.path.join(test_dir, 'orcc_1003.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 4.1430033493799998) self.assertAlmostEqual(conv.lattice.b, 31.437979757624728) self.assertAlmostEqual(conv.lattice.c, 3.99648651) parser = CifParser(os.path.join(test_dir, 'monoc_1028.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 117.53832420192903) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 14.033435583000625) self.assertAlmostEqual(conv.lattice.b, 3.96052850731) self.assertAlmostEqual(conv.lattice.c, 6.8743926325200002) parser = CifParser(os.path.join(test_dir, 'hex_1170.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 120) self.assertAlmostEqual(conv.lattice.a, 3.699919902005897) self.assertAlmostEqual(conv.lattice.b, 3.699919902005897) self.assertAlmostEqual(conv.lattice.c, 6.9779585500000003) def test_get_primitive_standard_structure(self): parser = CifParser(os.path.join(test_dir, 'bcc_1927.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) prim = s.get_primitive_standard_structure() self.assertAlmostEqual(prim.lattice.alpha, 109.47122063400001) self.assertAlmostEqual(prim.lattice.beta, 109.47122063400001) self.assertAlmostEqual(prim.lattice.gamma, 109.47122063400001) self.assertAlmostEqual(prim.lattice.a, 7.9657251015812145) self.assertAlmostEqual(prim.lattice.b, 7.9657251015812145) self.assertAlmostEqual(prim.lattice.c, 7.9657251015812145) parser = CifParser(os.path.join(test_dir, 'btet_1915.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) prim = s.get_primitive_standard_structure() self.assertAlmostEqual(prim.lattice.alpha, 105.015053349) self.assertAlmostEqual(prim.lattice.beta, 105.015053349) self.assertAlmostEqual(prim.lattice.gamma, 118.80658411899999) self.assertAlmostEqual(prim.lattice.a, 4.1579321075608791) self.assertAlmostEqual(prim.lattice.b, 4.1579321075608791) self.assertAlmostEqual(prim.lattice.c, 4.1579321075608791) parser = CifParser(os.path.join(test_dir, 'orci_1010.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) prim = s.get_primitive_standard_structure() self.assertAlmostEqual(prim.lattice.alpha, 134.78923546600001) self.assertAlmostEqual(prim.lattice.beta, 105.856239333) self.assertAlmostEqual(prim.lattice.gamma, 91.276341676000001) self.assertAlmostEqual(prim.lattice.a, 3.8428217771014852) self.assertAlmostEqual(prim.lattice.b, 3.8428217771014852) self.assertAlmostEqual(prim.lattice.c, 3.8428217771014852) parser = CifParser(os.path.join(test_dir, 'orcc_1003.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) prim = s.get_primitive_standard_structure() self.assertAlmostEqual(prim.lattice.alpha, 90) self.assertAlmostEqual(prim.lattice.beta, 90) self.assertAlmostEqual(prim.lattice.gamma, 164.985257335) self.assertAlmostEqual(prim.lattice.a, 15.854897098324196) self.assertAlmostEqual(prim.lattice.b, 15.854897098324196) self.assertAlmostEqual(prim.lattice.c, 3.99648651) parser = CifParser(os.path.join(test_dir, 'monoc_1028.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) prim = s.get_primitive_standard_structure() self.assertAlmostEqual(prim.lattice.alpha, 63.579155761999999) self.assertAlmostEqual(prim.lattice.beta, 116.42084423747779) self.assertAlmostEqual(prim.lattice.gamma, 148.47965136208569) self.assertAlmostEqual(prim.lattice.a, 7.2908007159612325) self.assertAlmostEqual(prim.lattice.b, 7.2908007159612325) self.assertAlmostEqual(prim.lattice.c, 6.8743926325200002) parser = CifParser(os.path.join(test_dir, 'hex_1170.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) prim = s.get_primitive_standard_structure() self.assertAlmostEqual(prim.lattice.alpha, 90) self.assertAlmostEqual(prim.lattice.beta, 90) self.assertAlmostEqual(prim.lattice.gamma, 120) self.assertAlmostEqual(prim.lattice.a, 3.699919902005897) self.assertAlmostEqual(prim.lattice.b, 3.699919902005897) self.assertAlmostEqual(prim.lattice.c, 6.9779585500000003) parser = CifParser(os.path.join(test_dir, 'rhomb_3478_conv.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) prim = s.get_primitive_standard_structure() self.assertAlmostEqual(prim.lattice.alpha, 28.049186140546812) self.assertAlmostEqual(prim.lattice.beta, 28.049186140546812) self.assertAlmostEqual(prim.lattice.gamma, 28.049186140546812) self.assertAlmostEqual(prim.lattice.a, 5.9352627428399982) self.assertAlmostEqual(prim.lattice.b, 5.9352627428399982) self.assertAlmostEqual(prim.lattice.c, 5.9352627428399982)
class HighSymmKpath(object): """ This class looks for path along high symmetry lines in the Brillouin Zone. It is based on Setyawan, W., & Curtarolo, S. (2010). High-throughput electronic band structure calculations: Challenges and tools. Computational Materials Science, 49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010 It should be used with primitive structures that comply with the definition from the paper. The symmetry is determined by spglib through the SpacegroupAnalyzer class. The analyzer can be used to produce the correct primitive structure (method get_primitive_standard_structure(international_monoclinic=False)). A warning will signal possible compatibility problems with the given structure. Args: structure (Structure): Structure object symprec (float): Tolerance for symmetry finding angle_tolerance (float): Angle tolerance for symmetry finding. atol (float): Absolute tolerance used to compare the input structure with the one expected as primitive standard. A warning will be issued if the lattices don't match. """ def __init__(self, structure, symprec=0.01, angle_tolerance=5, atol=1e-8): self._structure = structure self._sym = SpacegroupAnalyzer(structure, symprec=symprec, angle_tolerance=angle_tolerance) self._prim = self._sym\ .get_primitive_standard_structure(international_monoclinic=False) self._conv = self._sym.get_conventional_standard_structure(international_monoclinic=False) self._prim_rec = self._prim.lattice.reciprocal_lattice self._kpath = None #Note: this warning will be issued for space groups 38-41, since the primitive cell must be #reformatted to match Setyawan/Curtarolo convention in order to work with the current k-path #generation scheme. if not np.allclose(self._structure.lattice.matrix, self._prim.lattice.matrix, atol=atol): warnings.warn("The input structure does not match the expected standard primitive! " "The path can be incorrect. Use at your own risk.") lattice_type = self._sym.get_lattice_type() spg_symbol = self._sym.get_space_group_symbol() if lattice_type == "cubic": if "P" in spg_symbol: self._kpath = self.cubic() elif "F" in spg_symbol: self._kpath = self.fcc() elif "I" in spg_symbol: self._kpath = self.bcc() else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "tetragonal": if "P" in spg_symbol: self._kpath = self.tet() elif "I" in spg_symbol: a = self._conv.lattice.abc[0] c = self._conv.lattice.abc[2] if c < a: self._kpath = self.bctet1(c, a) else: self._kpath = self.bctet2(c, a) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "orthorhombic": a = self._conv.lattice.abc[0] b = self._conv.lattice.abc[1] c = self._conv.lattice.abc[2] if "P" in spg_symbol: self._kpath = self.orc() elif "F" in spg_symbol: if 1 / a ** 2 > 1 / b ** 2 + 1 / c ** 2: self._kpath = self.orcf1(a, b, c) elif 1 / a ** 2 < 1 / b ** 2 + 1 / c ** 2: self._kpath = self.orcf2(a, b, c) else: self._kpath = self.orcf3(a, b, c) elif "I" in spg_symbol: self._kpath = self.orci(a, b, c) elif "C" in spg_symbol or "A" in spg_symbol: self._kpath = self.orcc(a, b, c) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "hexagonal": self._kpath = self.hex() elif lattice_type == "rhombohedral": alpha = self._prim.lattice.lengths_and_angles[1][0] if alpha < 90: self._kpath = self.rhl1(alpha * pi / 180) else: self._kpath = self.rhl2(alpha * pi / 180) elif lattice_type == "monoclinic": a, b, c = self._conv.lattice.abc alpha = self._conv.lattice.lengths_and_angles[1][0] #beta = self._conv.lattice.lengths_and_angles[1][1] if "P" in spg_symbol: self._kpath = self.mcl(b, c, alpha * pi / 180) elif "C" in spg_symbol: kgamma = self._prim_rec.lengths_and_angles[1][2] if kgamma > 90: self._kpath = self.mclc1(a, b, c, alpha * pi / 180) if kgamma == 90: self._kpath = self.mclc2(a, b, c, alpha * pi / 180) if kgamma < 90: if b * cos(alpha * pi / 180) / c\ + b ** 2 * sin(alpha * pi / 180) ** 2 / a ** 2 < 1: self._kpath = self.mclc3(a, b, c, alpha * pi / 180) if b * cos(alpha * pi / 180) / c \ + b ** 2 * sin(alpha * pi / 180) ** 2 / a ** 2 == 1: self._kpath = self.mclc4(a, b, c, alpha * pi / 180) if b * cos(alpha * pi / 180) / c \ + b ** 2 * sin(alpha * pi / 180) ** 2 / a ** 2 > 1: self._kpath = self.mclc5(a, b, c, alpha * pi / 180) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "triclinic": kalpha = self._prim_rec.lengths_and_angles[1][0] kbeta = self._prim_rec.lengths_and_angles[1][1] kgamma = self._prim_rec.lengths_and_angles[1][2] if kalpha > 90 and kbeta > 90 and kgamma > 90: self._kpath = self.tria() if kalpha < 90 and kbeta < 90 and kgamma < 90: self._kpath = self.trib() if kalpha > 90 and kbeta > 90 and kgamma == 90: self._kpath = self.tria() if kalpha < 90 and kbeta < 90 and kgamma == 90: self._kpath = self.trib() else: warn("Unknown lattice type %s" % lattice_type) @property def structure(self): """ Returns: The standardized primitive structure """ return self._prim @property def conventional(self): """ Returns: The conventional cell structure """ return self._conv @property def prim(self): """ Returns: The primitive cell structure """ return self._prim @property def prim_rec(self): """ Returns: The primitive reciprocal cell structure """ return self._prim_rec @property def kpath(self): """ Returns: The symmetry line path in reciprocal space """ return self._kpath def get_kpoints(self, line_density=20, coords_are_cartesian=True): """ Returns: the kpoints along the paths in cartesian coordinates together with the labels for symmetry points -Wei """ list_k_points = [] sym_point_labels = [] for b in self.kpath['path']: for i in range(1, len(b)): start = np.array(self.kpath['kpoints'][b[i - 1]]) end = np.array(self.kpath['kpoints'][b[i]]) distance = np.linalg.norm( self._prim_rec.get_cartesian_coords(start) - self._prim_rec.get_cartesian_coords(end)) nb = int(ceil(distance * line_density)) sym_point_labels.extend([b[i - 1]] + [''] * (nb - 1) + [b[i]]) list_k_points.extend( [self._prim_rec.get_cartesian_coords(start) + float(i) / float(nb) * (self._prim_rec.get_cartesian_coords(end) - self._prim_rec.get_cartesian_coords(start)) for i in range(0, nb + 1)]) if coords_are_cartesian: return list_k_points, sym_point_labels else: frac_k_points = [self._prim_rec.get_fractional_coords(k) for k in list_k_points] return frac_k_points, sym_point_labels def cubic(self): self.name = "CUB" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'X': np.array([0.0, 0.5, 0.0]), 'R': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.5, 0.0])} path = [["\\Gamma", "X", "M", "\\Gamma", "R", "X"], ["M", "R"]] return {'kpoints': kpoints, 'path': path} def fcc(self): self.name = "FCC" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'K': np.array([3.0 / 8.0, 3.0 / 8.0, 3.0 / 4.0]), 'L': np.array([0.5, 0.5, 0.5]), 'U': np.array([5.0 / 8.0, 1.0 / 4.0, 5.0 / 8.0]), 'W': np.array([0.5, 1.0 / 4.0, 3.0 / 4.0]), 'X': np.array([0.5, 0.0, 0.5])} path = [["\\Gamma", "X", "W", "K", "\\Gamma", "L", "U", "W", "L", "K"], ["U", "X"]] return {'kpoints': kpoints, 'path': path} def bcc(self): self.name = "BCC" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'H': np.array([0.5, -0.5, 0.5]), 'P': np.array([0.25, 0.25, 0.25]), 'N': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "H", "N", "\\Gamma", "P", "H"], ["P", "N"]] return {'kpoints': kpoints, 'path': path} def tet(self): self.name = "TET" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.5, 0.0]), 'R': np.array([0.0, 0.5, 0.5]), 'X': np.array([0.0, 0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "X", "M", "\\Gamma", "Z", "R", "A", "Z"], ["X", "R"], ["M", "A"]] return {'kpoints': kpoints, 'path': path} def bctet1(self, c, a): self.name = "BCT1" eta = (1 + c ** 2 / a ** 2) / 4.0 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'M': np.array([-0.5, 0.5, 0.5]), 'N': np.array([0.0, 0.5, 0.0]), 'P': np.array([0.25, 0.25, 0.25]), 'X': np.array([0.0, 0.0, 0.5]), 'Z': np.array([eta, eta, -eta]), 'Z_1': np.array([-eta, 1 - eta, eta])} path = [["\\Gamma", "X", "M", "\\Gamma", "Z", "P", "N", "Z_1", "M"], ["X", "P"]] return {'kpoints': kpoints, 'path': path} def bctet2(self, c, a): self.name = "BCT2" eta = (1 + a ** 2 / c ** 2) / 4.0 zeta = a ** 2 / (2 * c ** 2) kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'N': np.array([0.0, 0.5, 0.0]), 'P': np.array([0.25, 0.25, 0.25]), '\\Sigma': np.array([-eta, eta, eta]), '\\Sigma_1': np.array([eta, 1 - eta, -eta]), 'X': np.array([0.0, 0.0, 0.5]), 'Y': np.array([-zeta, zeta, 0.5]), 'Y_1': np.array([0.5, 0.5, -zeta]), 'Z': np.array([0.5, 0.5, -0.5])} path = [["\\Gamma", "X", "Y", "\\Sigma", "\\Gamma", "Z", "\\Sigma_1", "N", "P", "Y_1", "Z"], ["X", "P"]] return {'kpoints': kpoints, 'path': path} def orc(self): self.name = "ORC" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'R': np.array([0.5, 0.5, 0.5]), 'S': np.array([0.5, 0.5, 0.0]), 'T': np.array([0.0, 0.5, 0.5]), 'U': np.array([0.5, 0.0, 0.5]), 'X': np.array([0.5, 0.0, 0.0]), 'Y': np.array([0.0, 0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "X", "S", "Y", "\\Gamma", "Z", "U", "R", "T", "Z"], ["Y", "T"], ["U", "X"], ["S", "R"]] return {'kpoints': kpoints, 'path': path} def orcf1(self, a, b, c): self.name = "ORCF1" zeta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 eta = (1 + a ** 2 / b ** 2 + a ** 2 / c ** 2) / 4 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5 + zeta, zeta]), 'A_1': np.array([0.5, 0.5 - zeta, 1 - zeta]), 'L': np.array([0.5, 0.5, 0.5]), 'T': np.array([1, 0.5, 0.5]), 'X': np.array([0.0, eta, eta]), 'X_1': np.array([1, 1 - eta, 1 - eta]), 'Y': np.array([0.5, 0.0, 0.5]), 'Z': np.array([0.5, 0.5, 0.0])} path = [["\\Gamma", "Y", "T", "Z", "\\Gamma", "X", "A_1", "Y"], ["T", "X_1"], ["X", "A", "Z"], ["L", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def orcf2(self, a, b, c): self.name = "ORCF2" phi = (1 + c ** 2 / b ** 2 - c ** 2 / a ** 2) / 4 eta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 delta = (1 + b ** 2 / a ** 2 - b ** 2 / c ** 2) / 4 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'C': np.array([0.5, 0.5 - eta, 1 - eta]), 'C_1': np.array([0.5, 0.5 + eta, eta]), 'D': np.array([0.5 - delta, 0.5, 1 - delta]), 'D_1': np.array([0.5 + delta, 0.5, delta]), 'L': np.array([0.5, 0.5, 0.5]), 'H': np.array([1 - phi, 0.5 - phi, 0.5]), 'H_1': np.array([phi, 0.5 + phi, 0.5]), 'X': np.array([0.0, 0.5, 0.5]), 'Y': np.array([0.5, 0.0, 0.5]), 'Z': np.array([0.5, 0.5, 0.0])} path = [["\\Gamma", "Y", "C", "D", "X", "\\Gamma", "Z", "D_1", "H", "C"], ["C_1", "Z"], ["X", "H_1"], ["H", "Y"], ["L", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def orcf3(self, a, b, c): self.name = "ORCF3" zeta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 eta = (1 + a ** 2 / b ** 2 + a ** 2 / c ** 2) / 4 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5 + zeta, zeta]), 'A_1': np.array([0.5, 0.5 - zeta, 1 - zeta]), 'L': np.array([0.5, 0.5, 0.5]), 'T': np.array([1, 0.5, 0.5]), 'X': np.array([0.0, eta, eta]), 'X_1': np.array([1, 1 - eta, 1 - eta]), 'Y': np.array([0.5, 0.0, 0.5]), 'Z': np.array([0.5, 0.5, 0.0])} path = [["\\Gamma", "Y", "T", "Z", "\\Gamma", "X", "A_1", "Y"], ["X", "A", "Z"], ["L", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def orci(self, a, b, c): self.name = "ORCI" zeta = (1 + a ** 2 / c ** 2) / 4 eta = (1 + b ** 2 / c ** 2) / 4 delta = (b ** 2 - a ** 2) / (4 * c ** 2) mu = (a ** 2 + b ** 2) / (4 * c ** 2) kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'L': np.array([-mu, mu, 0.5 - delta]), 'L_1': np.array([mu, -mu, 0.5 + delta]), 'L_2': np.array([0.5 - delta, 0.5 + delta, -mu]), 'R': np.array([0.0, 0.5, 0.0]), 'S': np.array([0.5, 0.0, 0.0]), 'T': np.array([0.0, 0.0, 0.5]), 'W': np.array([0.25, 0.25, 0.25]), 'X': np.array([-zeta, zeta, zeta]), 'X_1': np.array([zeta, 1 - zeta, -zeta]), 'Y': np.array([eta, -eta, eta]), 'Y_1': np.array([1 - eta, eta, -eta]), 'Z': np.array([0.5, 0.5, -0.5])} path = [["\\Gamma", "X", "L", "T", "W", "R", "X_1", "Z", "\\Gamma", "Y", "S", "W"], ["L_1", "Y"], ["Y_1", "Z"]] return {'kpoints': kpoints, 'path': path} def orcc(self, a, b, c): self.name = "ORCC" zeta = (1 + a ** 2 / b ** 2) / 4 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([zeta, zeta, 0.5]), 'A_1': np.array([-zeta, 1 - zeta, 0.5]), 'R': np.array([0.0, 0.5, 0.5]), 'S': np.array([0.0, 0.5, 0.0]), 'T': np.array([-0.5, 0.5, 0.5]), 'X': np.array([zeta, zeta, 0.0]), 'X_1': np.array([-zeta, 1 - zeta, 0.0]), 'Y': np.array([-0.5, 0.5, 0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "X", "S", "R", "A", "Z", "\\Gamma", "Y", "X_1", "A_1", "T", "Y"], ["Z", "T"]] return {'kpoints': kpoints, 'path': path} def hex(self): self.name = "HEX" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.0, 0.0, 0.5]), 'H': np.array([1.0 / 3.0, 1.0 / 3.0, 0.5]), 'K': np.array([1.0 / 3.0, 1.0 / 3.0, 0.0]), 'L': np.array([0.5, 0.0, 0.5]), 'M': np.array([0.5, 0.0, 0.0])} path = [["\\Gamma", "M", "K", "\\Gamma", "A", "L", "H", "A"], ["L", "M"], ["K", "H"]] return {'kpoints': kpoints, 'path': path} def rhl1(self, alpha): self.name = "RHL1" eta = (1 + 4 * cos(alpha)) / (2 + 4 * cos(alpha)) nu = 3.0 / 4.0 - eta / 2.0 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'B': np.array([eta, 0.5, 1.0 - eta]), 'B_1': np.array([1.0 / 2.0, 1.0 - eta, eta - 1.0]), 'F': np.array([0.5, 0.5, 0.0]), 'L': np.array([0.5, 0.0, 0.0]), 'L_1': np.array([0.0, 0.0, -0.5]), 'P': np.array([eta, nu, nu]), 'P_1': np.array([1.0 - nu, 1.0 - nu, 1.0 - eta]), 'P_2': np.array([nu, nu, eta - 1.0]), 'Q': np.array([1.0 - nu, nu, 0.0]), 'X': np.array([nu, 0.0, -nu]), 'Z': np.array([0.5, 0.5, 0.5])} path = [["\\Gamma", "L", "B_1"], ["B", "Z", "\\Gamma", "X"], ["Q", "F", "P_1", "Z"], ["L", "P"]] return {'kpoints': kpoints, 'path': path} def rhl2(self, alpha): self.name = "RHL2" eta = 1 / (2 * tan(alpha / 2.0) ** 2) nu = 3.0 / 4.0 - eta / 2.0 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([0.5, -0.5, 0.0]), 'L': np.array([0.5, 0.0, 0.0]), 'P': np.array([1 - nu, -nu, 1 - nu]), 'P_1': np.array([nu, nu - 1.0, nu - 1.0]), 'Q': np.array([eta, eta, eta]), 'Q_1': np.array([1.0 - eta, -eta, -eta]), 'Z': np.array([0.5, -0.5, 0.5])} path = [["\\Gamma", "P", "Z", "Q", "\\Gamma", "F", "P_1", "Q_1", "L", "Z"]] return {'kpoints': kpoints, 'path': path} def mcl(self, b, c, beta): self.name = "MCL" eta = (1 - b * cos(beta) / c) / (2 * sin(beta) ** 2) nu = 0.5 - eta * c * cos(beta) / b kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5, 0.0]), 'C': np.array([0.0, 0.5, 0.5]), 'D': np.array([0.5, 0.0, 0.5]), 'D_1': np.array([0.5, 0.5, -0.5]), 'E': np.array([0.5, 0.5, 0.5]), 'H': np.array([0.0, eta, 1.0 - nu]), 'H_1': np.array([0.0, 1.0 - eta, nu]), 'H_2': np.array([0.0, eta, -nu]), 'M': np.array([0.5, eta, 1.0 - nu]), 'M_1': np.array([0.5, 1 - eta, nu]), 'M_2': np.array([0.5, 1 - eta, nu]), 'X': np.array([0.0, 0.5, 0.0]), 'Y': np.array([0.0, 0.0, 0.5]), 'Y_1': np.array([0.0, 0.0, -0.5]), 'Z': np.array([0.5, 0.0, 0.0])} path = [["\\Gamma", "Y", "H", "C", "E", "M_1", "A", "X", "H_1"], ["M", "D", "Z"], ["Y", "D"]] return {'kpoints': kpoints, 'path': path} def mclc1(self, a, b, c, alpha): self.name = "MCLC1" zeta = (2 - b * cos(alpha) / c) / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b psi = 0.75 - a ** 2 / (4 * b ** 2 * sin(alpha) ** 2) phi = psi + (0.75 - psi) * b * cos(alpha) / c kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'F': np.array([1 - zeta, 1 - zeta, 1 - eta]), 'F_1': np.array([zeta, zeta, eta]), 'F_2': np.array([-zeta, -zeta, 1 - eta]), #'F_3': np.array([1 - zeta, -zeta, 1 - eta]), 'I': np.array([phi, 1 - phi, 0.5]), 'I_1': np.array([1 - phi, phi - 1, 0.5]), 'L': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'X': np.array([1 - psi, psi - 1, 0.0]), 'X_1': np.array([psi, 1 - psi, 0.0]), 'X_2': np.array([psi - 1, -psi, 0.0]), 'Y': np.array([0.5, 0.5, 0.0]), 'Y_1': np.array([-0.5, -0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "F_1"], ["Y", "X_1"], ["X", "\\Gamma", "N"], ["M", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def mclc2(self, a, b, c, alpha): self.name = "MCLC2" zeta = (2 - b * cos(alpha) / c) / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b psi = 0.75 - a ** 2 / (4 * b ** 2 * sin(alpha) ** 2) phi = psi + (0.75 - psi) * b * cos(alpha) / c kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'F': np.array([1 - zeta, 1 - zeta, 1 - eta]), 'F_1': np.array([zeta, zeta, eta]), 'F_2': np.array([-zeta, -zeta, 1 - eta]), 'F_3': np.array([1 - zeta, -zeta, 1 - eta]), 'I': np.array([phi, 1 - phi, 0.5]), 'I_1': np.array([1 - phi, phi - 1, 0.5]), 'L': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'X': np.array([1 - psi, psi - 1, 0.0]), 'X_1': np.array([psi, 1 - psi, 0.0]), 'X_2': np.array([psi - 1, -psi, 0.0]), 'Y': np.array([0.5, 0.5, 0.0]), 'Y_1': np.array([-0.5, -0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "F_1"], ["N", "\\Gamma", "M"]] return {'kpoints': kpoints, 'path': path} def mclc3(self, a, b, c, alpha): self.name = "MCLC3" mu = (1 + b ** 2 / a ** 2) / 4.0 delta = b * c * cos(alpha) / (2 * a ** 2) zeta = mu - 0.25 + (1 - b * cos(alpha) / c)\ / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b phi = 1 + zeta - 2 * mu psi = eta - 2 * delta kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([1 - phi, 1 - phi, 1 - psi]), 'F_1': np.array([phi, phi - 1, psi]), 'F_2': np.array([1 - phi, -phi, 1 - psi]), 'H': np.array([zeta, zeta, eta]), 'H_1': np.array([1 - zeta, -zeta, 1 - eta]), 'H_2': np.array([-zeta, -zeta, 1 - eta]), 'I': np.array([0.5, -0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'X': np.array([0.5, -0.5, 0.0]), 'Y': np.array([mu, mu, delta]), 'Y_1': np.array([1 - mu, -mu, -delta]), 'Y_2': np.array([-mu, -mu, -delta]), 'Y_3': np.array([mu, mu - 1, delta]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "Y", "F", "H", "Z", "I", "F_1"], ["H_1", "Y_1", "X", "\\Gamma", "N"], ["M", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def mclc4(self, a, b, c, alpha): self.name = "MCLC4" mu = (1 + b ** 2 / a ** 2) / 4.0 delta = b * c * cos(alpha) / (2 * a ** 2) zeta = mu - 0.25 + (1 - b * cos(alpha) / c)\ / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b phi = 1 + zeta - 2 * mu psi = eta - 2 * delta kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([1 - phi, 1 - phi, 1 - psi]), 'F_1': np.array([phi, phi - 1, psi]), 'F_2': np.array([1 - phi, -phi, 1 - psi]), 'H': np.array([zeta, zeta, eta]), 'H_1': np.array([1 - zeta, -zeta, 1 - eta]), 'H_2': np.array([-zeta, -zeta, 1 - eta]), 'I': np.array([0.5, -0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'X': np.array([0.5, -0.5, 0.0]), 'Y': np.array([mu, mu, delta]), 'Y_1': np.array([1 - mu, -mu, -delta]), 'Y_2': np.array([-mu, -mu, -delta]), 'Y_3': np.array([mu, mu - 1, delta]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "Y", "F", "H", "Z", "I"], ["H_1", "Y_1", "X", "\\Gamma", "N"], ["M", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def mclc5(self, a, b, c, alpha): self.name = "MCLC5" zeta = (b ** 2 / a ** 2 + (1 - b * cos(alpha) / c) / sin(alpha) ** 2) / 4 eta = 0.5 + 2 * zeta * c * cos(alpha) / b mu = eta / 2 + b ** 2 / (4 * a ** 2) \ - b * c * cos(alpha) / (2 * a ** 2) nu = 2 * mu - zeta rho = 1 - zeta * a ** 2 / b ** 2 omega = (4 * nu - 1 - b ** 2 * sin(alpha) ** 2 / a ** 2)\ * c / (2 * b * cos(alpha)) delta = zeta * c * cos(alpha) / b + omega / 2 - 0.25 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([nu, nu, omega]), 'F_1': np.array([1 - nu, 1 - nu, 1 - omega]), 'F_2': np.array([nu, nu - 1, omega]), 'H': np.array([zeta, zeta, eta]), 'H_1': np.array([1 - zeta, -zeta, 1 - eta]), 'H_2': np.array([-zeta, -zeta, 1 - eta]), 'I': np.array([rho, 1 - rho, 0.5]), 'I_1': np.array([1 - rho, rho - 1, 0.5]), 'L': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'X': np.array([0.5, -0.5, 0.0]), 'Y': np.array([mu, mu, delta]), 'Y_1': np.array([1 - mu, -mu, -delta]), 'Y_2': np.array([-mu, -mu, -delta]), 'Y_3': np.array([mu, mu - 1, delta]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "H", "F_1"], ["H_1", "Y_1", "X", "\\Gamma", "N"], ["M", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def tria(self): self.name = "TRI1a" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'L': np.array([0.5, 0.5, 0.0]), 'M': np.array([0.0, 0.5, 0.5]), 'N': np.array([0.5, 0.0, 0.5]), 'R': np.array([0.5, 0.5, 0.5]), 'X': np.array([0.5, 0.0, 0.0]), 'Y': np.array([0.0, 0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["X", "\\Gamma", "Y"], ["L", "\\Gamma", "Z"], ["N", "\\Gamma", "M"], ["R", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def trib(self): self.name = "TRI1b" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'L': np.array([0.5, -0.5, 0.0]), 'M': np.array([0.0, 0.0, 0.5]), 'N': np.array([-0.5, -0.5, 0.5]), 'R': np.array([0.0, -0.5, 0.5]), 'X': np.array([0.0, -0.5, 0.0]), 'Y': np.array([0.5, 0.0, 0.0]), 'Z': np.array([-0.5, 0.0, 0.5])} path = [["X", "\\Gamma", "Y"], ["L", "\\Gamma", "Z"], ["N", "\\Gamma", "M"], ["R", "\\Gamma"]] return {'kpoints': kpoints, 'path': path}
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) def round_symm_trans(i): for t in TRANSLATIONS.values(): if abs(i - t) < symprec: return t if abs(i - round(i)) < symprec: return 0 raise ValueError("Invalid translation!") symmops = [] for op in sf.get_symmetry_operations(): v = op.translation_vector v = [round_symm_trans(i) for i in v] 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 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)
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_space_group_symbol(), fitter.get_space_group_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.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) return finder.get_space_group_number() target_sgnum = get_sg_info(symmetrized_structure.sites) curr_sites = list(itertools.chain.from_iterable(disordered_sites)) sgnum = get_sg_info(curr_sites) ordered_sites = sorted(ordered_sites, key=lambda sites: len(sites)) logger.debug("Disordered sites has sg # %d" % (sgnum)) self.ordered_sites = [] # progressively add ordered sites to our disordered sites # until we match the symmetry of our input structure if self.check_ordered_symmetry: while sgnum != target_sgnum and len(ordered_sites) > 0: sites = ordered_sites.pop(0) temp_sites = list(curr_sites) + sites new_sgnum = get_sg_info(temp_sites) if sgnum != new_sgnum: logger.debug("Adding %s in enum. New sg # %d" % (sites[0].specie, new_sgnum)) index_species.append(sites[0].specie) index_amounts.append(len(sites)) sp_label = len(index_species) - 1 for site in sites: coord_str.append("{} {}".format( coord_format.format(*site.coords), sp_label)) disordered_sites.append(sites) curr_sites = temp_sites sgnum = new_sgnum else: self.ordered_sites.extend(sites) for sites in ordered_sites: 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("%d" % len(index_species)) output.append("%d" % 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 = int(ndisordered*lcm(*[f.limit_denominator(ndisordered * self.max_cell_size).denominator for f in map(fractions.Fraction, index_amounts)])) # This multiplicative factor of 10 is to prevent having too small bases # which can lead to rounding issues in the next step. # An old bug was that a base was set to 8, with a conc of 0.4:0.6. That # resulted in a range that overlaps and a conc of 0.5 satisfying this # enumeration. See Cu7Te5.cif test file. base *= 10 # 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))
class HighSymmKpath(object): """ This class looks for path along high symmetry lines in the Brillouin Zone. It is based on Setyawan, W., & Curtarolo, S. (2010). High-throughput electronic band structure calculations: Challenges and tools. Computational Materials Science, 49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010 The symmetry is determined by spglib through the SpacegroupAnalyzer class Args: structure (Structure): Structure object symprec (float): Tolerance for symmetry finding angle_tolerance (float): Angle tolerance for symmetry finding. """ def __init__(self, structure, symprec=0.01, angle_tolerance=5): self._structure = structure self._sym = SpacegroupAnalyzer(structure, symprec=symprec, angle_tolerance=angle_tolerance) self._prim = self._sym.get_primitive_standard_structure(international_monoclinic=False) self._conv = self._sym.get_conventional_standard_structure(international_monoclinic=False) self._prim_rec = self._prim.lattice.reciprocal_lattice self._kpath = None lattice_type = self._sym.get_lattice_type() spg_symbol = self._sym.get_space_group_symbol() if lattice_type == "cubic": if "P" in spg_symbol: self._kpath = self.cubic() elif "F" in spg_symbol: self._kpath = self.fcc() elif "I" in spg_symbol: self._kpath = self.bcc() else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "tetragonal": if "P" in spg_symbol: self._kpath = self.tet() elif "I" in spg_symbol: a = self._conv.lattice.abc[0] c = self._conv.lattice.abc[2] if c < a: self._kpath = self.bctet1(c, a) else: self._kpath = self.bctet2(c, a) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "orthorhombic": a = self._conv.lattice.abc[0] b = self._conv.lattice.abc[1] c = self._conv.lattice.abc[2] if "P" in spg_symbol: self._kpath = self.orc() elif "F" in spg_symbol: if 1 / a ** 2 > 1 / b ** 2 + 1 / c ** 2: self._kpath = self.orcf1(a, b, c) elif 1 / a ** 2 < 1 / b ** 2 + 1 / c ** 2: self._kpath = self.orcf2(a, b, c) else: self._kpath = self.orcf3(a, b, c) elif "I" in spg_symbol: self._kpath = self.orci(a, b, c) elif "C" in spg_symbol: self._kpath = self.orcc(a, b, c) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "hexagonal": self._kpath = self.hex() elif lattice_type == "rhombohedral": alpha = self._prim.lattice.lengths_and_angles[1][0] if alpha < 90: self._kpath = self.rhl1(alpha * pi / 180) else: self._kpath = self.rhl2(alpha * pi / 180) elif lattice_type == "monoclinic": a, b, c = self._conv.lattice.abc alpha = self._conv.lattice.lengths_and_angles[1][0] # beta = self._conv.lattice.lengths_and_angles[1][1] if "P" in spg_symbol: self._kpath = self.mcl(b, c, alpha * pi / 180) elif "C" in spg_symbol: kgamma = self._prim_rec.lengths_and_angles[1][2] if kgamma > 90: self._kpath = self.mclc1(a, b, c, alpha * pi / 180) if kgamma == 90: self._kpath = self.mclc2(a, b, c, alpha * pi / 180) if kgamma < 90: if b * cos(alpha * pi / 180) / c + b ** 2 * sin(alpha) ** 2 / a ** 2 < 1: self._kpath = self.mclc3(a, b, c, alpha * pi / 180) if b * cos(alpha * pi / 180) / c + b ** 2 * sin(alpha) ** 2 / a ** 2 == 1: self._kpath = self.mclc4(a, b, c, alpha * pi / 180) if b * cos(alpha * pi / 180) / c + b ** 2 * sin(alpha) ** 2 / a ** 2 > 1: self._kpath = self.mclc5(a, b, c, alpha * pi / 180) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "triclinic": kalpha = self._prim_rec.lengths_and_angles[1][0] kbeta = self._prim_rec.lengths_and_angles[1][1] kgamma = self._prim_rec.lengths_and_angles[1][2] if kalpha > 90 and kbeta > 90 and kgamma > 90: self._kpath = self.tria() if kalpha < 90 and kbeta < 90 and kgamma < 90: self._kpath = self.trib() if kalpha > 90 and kbeta > 90 and kgamma == 90: self._kpath = self.tria() if kalpha < 90 and kbeta < 90 and kgamma == 90: self._kpath = self.trib() else: warn("Unknown lattice type %s" % lattice_type) @property def structure(self): """ Returns: The standardized primitive structure """ return self._prim @property def conventional(self): """ Returns: The conventional cell structure """ return self._conv @property def prim(self): """ Returns: The primitive cell structure """ return self._prim @property def prim_rec(self): """ Returns: The primitive reciprocal cell structure """ return self._prim_rec @property def kpath(self): """ Returns: The symmetry line path in reciprocal space """ return self._kpath def get_kpoints(self, line_density=20, coords_are_cartesian=True): """ Returns: the kpoints along the paths in cartesian coordinates together with the labels for symmetry points -Wei """ list_k_points = [] sym_point_labels = [] for b in self.kpath["path"]: for i in range(1, len(b)): start = np.array(self.kpath["kpoints"][b[i - 1]]) end = np.array(self.kpath["kpoints"][b[i]]) distance = np.linalg.norm( self._prim_rec.get_cartesian_coords(start) - self._prim_rec.get_cartesian_coords(end) ) nb = int(ceil(distance * line_density)) sym_point_labels.extend([b[i - 1]] + [""] * (nb - 1) + [b[i]]) list_k_points.extend( [ self._prim_rec.get_cartesian_coords(start) + float(i) / float(nb) * (self._prim_rec.get_cartesian_coords(end) - self._prim_rec.get_cartesian_coords(start)) for i in range(0, nb + 1) ] ) if coords_are_cartesian: return list_k_points, sym_point_labels else: frac_k_points = [self._prim_rec.get_fractional_coords(k) for k in list_k_points] return frac_k_points, sym_point_labels def cubic(self): self.name = "CUB" kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "X": np.array([0.0, 0.5, 0.0]), "R": np.array([0.5, 0.5, 0.5]), "M": np.array([0.5, 0.5, 0.0]), } path = [["\Gamma", "X", "M", "\Gamma", "R", "X"], ["M", "R"]] return {"kpoints": kpoints, "path": path} def fcc(self): self.name = "FCC" kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "K": np.array([3.0 / 8.0, 3.0 / 8.0, 3.0 / 4.0]), "L": np.array([0.5, 0.5, 0.5]), "U": np.array([5.0 / 8.0, 1.0 / 4.0, 5.0 / 8.0]), "W": np.array([0.5, 1.0 / 4.0, 3.0 / 4.0]), "X": np.array([0.5, 0.0, 0.5]), } path = [["\Gamma", "X", "W", "K", "\Gamma", "L", "U", "W", "L", "K"], ["U", "X"]] return {"kpoints": kpoints, "path": path} def bcc(self): self.name = "BCC" kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "H": np.array([0.5, -0.5, 0.5]), "P": np.array([0.25, 0.25, 0.25]), "N": np.array([0.0, 0.0, 0.5]), } path = [["\Gamma", "H", "N", "\Gamma", "P", "H"], ["P", "N"]] return {"kpoints": kpoints, "path": path} def tet(self): self.name = "TET" kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "A": np.array([0.5, 0.5, 0.5]), "M": np.array([0.5, 0.5, 0.0]), "R": np.array([0.0, 0.5, 0.5]), "X": np.array([0.0, 0.5, 0.0]), "Z": np.array([0.0, 0.0, 0.5]), } path = [["\Gamma", "X", "M", "\Gamma", "Z", "R", "A", "Z"], ["X", "R"], ["M", "A"]] return {"kpoints": kpoints, "path": path} def bctet1(self, c, a): self.name = "BCT1" eta = (1 + c ** 2 / a ** 2) / 4.0 kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "M": np.array([-0.5, 0.5, 0.5]), "N": np.array([0.0, 0.5, 0.0]), "P": np.array([0.25, 0.25, 0.25]), "X": np.array([0.0, 0.0, 0.5]), "Z": np.array([eta, eta, -eta]), "Z_1": np.array([-eta, 1 - eta, eta]), } path = [["\Gamma", "X", "M", "\Gamma", "Z", "P", "N", "Z_1", "M"], ["X", "P"]] return {"kpoints": kpoints, "path": path} def bctet2(self, c, a): self.name = "BCT2" eta = (1 + a ** 2 / c ** 2) / 4.0 zeta = a ** 2 / (2 * c ** 2) kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "N": np.array([0.0, 0.5, 0.0]), "P": np.array([0.25, 0.25, 0.25]), "\Sigma": np.array([-eta, eta, eta]), "\Sigma_1": np.array([eta, 1 - eta, -eta]), "X": np.array([0.0, 0.0, 0.5]), "Y": np.array([-zeta, zeta, 0.5]), "Y_1": np.array([0.5, 0.5, -zeta]), "Z": np.array([0.5, 0.5, -0.5]), } path = [["\Gamma", "X", "Y", "\Sigma", "\Gamma", "Z", "\Sigma_1", "N", "P", "Y_1", "Z"], ["X", "P"]] return {"kpoints": kpoints, "path": path} def orc(self): self.name = "ORC" kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "R": np.array([0.5, 0.5, 0.5]), "S": np.array([0.5, 0.5, 0.0]), "T": np.array([0.0, 0.5, 0.5]), "U": np.array([0.5, 0.0, 0.5]), "X": np.array([0.5, 0.0, 0.0]), "Y": np.array([0.0, 0.5, 0.0]), "Z": np.array([0.0, 0.0, 0.5]), } path = [["\Gamma", "X", "S", "Y", "\Gamma", "Z", "U", "R", "T", "Z"], ["Y", "T"], ["U", "X"], ["S", "R"]] return {"kpoints": kpoints, "path": path} def orcf1(self, a, b, c): self.name = "ORCF1" zeta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 eta = (1 + a ** 2 / b ** 2 + a ** 2 / c ** 2) / 4 kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "A": np.array([0.5, 0.5 + zeta, zeta]), "A_1": np.array([0.5, 0.5 - zeta, 1 - zeta]), "L": np.array([0.5, 0.5, 0.5]), "T": np.array([1, 0.5, 0.5]), "X": np.array([0.0, eta, eta]), "X_1": np.array([1, 1 - eta, 1 - eta]), "Y": np.array([0.5, 0.0, 0.5]), "Z": np.array([0.5, 0.5, 0.0]), } path = [["\Gamma", "Y", "T", "Z", "\Gamma", "X", "A_1", "Y"], ["T", "X_1"], ["X", "A", "Z"], ["L", "\Gamma"]] return {"kpoints": kpoints, "path": path} def orcf2(self, a, b, c): self.name = "ORCF2" phi = (1 + c ** 2 / b ** 2 - c ** 2 / a ** 2) / 4 eta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 delta = (1 + b ** 2 / a ** 2 - b ** 2 / c ** 2) / 4 kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "C": np.array([0.5, 0.5 - eta, 1 - eta]), "C_1": np.array([0.5, 0.5 + eta, eta]), "D": np.array([0.5 - delta, 0.5, 1 - delta]), "D_1": np.array([0.5 + delta, 0.5, delta]), "L": np.array([0.5, 0.5, 0.5]), "H": np.array([1 - phi, 0.5 - phi, 0.5]), "H_1": np.array([phi, 0.5 + phi, 0.5]), "X": np.array([0.0, 0.5, 0.5]), "Y": np.array([0.5, 0.0, 0.5]), "Z": np.array([0.5, 0.5, 0.0]), } path = [ ["\Gamma", "Y", "C", "D", "X", "\Gamma", "Z", "D_1", "H", "C"], ["C_1", "Z"], ["X", "H_1"], ["H", "Y"], ["L", "\Gamma"], ] return {"kpoints": kpoints, "path": path} def orcf3(self, a, b, c): self.name = "ORCF3" zeta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 eta = (1 + a ** 2 / b ** 2 + a ** 2 / c ** 2) / 4 kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "A": np.array([0.5, 0.5 + zeta, zeta]), "A_1": np.array([0.5, 0.5 - zeta, 1 - zeta]), "L": np.array([0.5, 0.5, 0.5]), "T": np.array([1, 0.5, 0.5]), "X": np.array([0.0, eta, eta]), "X_1": np.array([1, 1 - eta, 1 - eta]), "Y": np.array([0.5, 0.0, 0.5]), "Z": np.array([0.5, 0.5, 0.0]), } path = [["\Gamma", "Y", "T", "Z", "\Gamma", "X", "A_1", "Y"], ["X", "A", "Z"], ["L", "\Gamma"]] return {"kpoints": kpoints, "path": path} def orci(self, a, b, c): self.name = "ORCI" zeta = (1 + a ** 2 / c ** 2) / 4 eta = (1 + b ** 2 / c ** 2) / 4 delta = (b ** 2 - a ** 2) / (4 * c ** 2) mu = (a ** 2 + b ** 2) / (4 * c ** 2) kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "L": np.array([-mu, mu, 0.5 - delta]), "L_1": np.array([mu, -mu, 0.5 + delta]), "L_2": np.array([0.5 - delta, 0.5 + delta, -mu]), "R": np.array([0.0, 0.5, 0.0]), "S": np.array([0.5, 0.0, 0.0]), "T": np.array([0.0, 0.0, 0.5]), "W": np.array([0.25, 0.25, 0.25]), "X": np.array([-zeta, zeta, zeta]), "X_1": np.array([zeta, 1 - zeta, -zeta]), "Y": np.array([eta, -eta, eta]), "Y_1": np.array([1 - eta, eta, -eta]), "Z": np.array([0.5, 0.5, -0.5]), } path = [["\Gamma", "X", "L", "T", "W", "R", "X_1", "Z", "\Gamma", "Y", "S", "W"], ["L_1", "Y"], ["Y_1", "Z"]] return {"kpoints": kpoints, "path": path} def orcc(self, a, b, c): self.name = "ORCC" zeta = (1 + a ** 2 / b ** 2) / 4 kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "A": np.array([zeta, zeta, 0.5]), "A_1": np.array([-zeta, 1 - zeta, 0.5]), "R": np.array([0.0, 0.5, 0.5]), "S": np.array([0.0, 0.5, 0.0]), "T": np.array([-0.5, 0.5, 0.5]), "X": np.array([zeta, zeta, 0.0]), "X_1": np.array([-zeta, 1 - zeta, 0.0]), "Y": np.array([-0.5, 0.5, 0]), "Z": np.array([0.0, 0.0, 0.5]), } path = [["\Gamma", "X", "S", "R", "A", "Z", "\Gamma", "Y", "X_1", "A_1", "T", "Y"], ["Z", "T"]] return {"kpoints": kpoints, "path": path} def hex(self): self.name = "HEX" kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "A": np.array([0.0, 0.0, 0.5]), "H": np.array([1.0 / 3.0, 1.0 / 3.0, 0.5]), "K": np.array([1.0 / 3.0, 1.0 / 3.0, 0.0]), "L": np.array([0.5, 0.0, 0.5]), "M": np.array([0.5, 0.0, 0.0]), } path = [["\Gamma", "M", "K", "\Gamma", "A", "L", "H", "A"], ["L", "M"], ["K", "H"]] return {"kpoints": kpoints, "path": path} def rhl1(self, alpha): self.name = "RHL1" eta = (1 + 4 * cos(alpha)) / (2 + 4 * cos(alpha)) nu = 3.0 / 4.0 - eta / 2.0 kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "B": np.array([eta, 0.5, 1.0 - eta]), "B_1": np.array([1.0 / 2.0, 1.0 - eta, eta - 1.0]), "F": np.array([0.5, 0.5, 0.0]), "L": np.array([0.5, 0.0, 0.0]), "L_1": np.array([0.0, 0.0, -0.5]), "P": np.array([eta, nu, nu]), "P_1": np.array([1.0 - nu, 1.0 - nu, 1.0 - eta]), "P_2": np.array([nu, nu, eta - 1.0]), "Q": np.array([1.0 - nu, nu, 0.0]), "X": np.array([nu, 0.0, -nu]), "Z": np.array([0.5, 0.5, 0.5]), } path = [["\Gamma", "L", "B_1"], ["B", "Z", "\Gamma", "X"], ["Q", "F", "P_1", "Z"], ["L", "P"]] return {"kpoints": kpoints, "path": path} def rhl2(self, alpha): self.name = "RHL2" eta = 1 / (2 * tan(alpha / 2.0) ** 2) nu = 3.0 / 4.0 - eta / 2.0 kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "F": np.array([0.5, -0.5, 0.0]), "L": np.array([0.5, 0.0, 0.0]), "P": np.array([1 - nu, -nu, 1 - nu]), "P_1": np.array([nu, nu - 1.0, nu - 1.0]), "Q": np.array([eta, eta, eta]), "Q_1": np.array([1.0 - eta, -eta, -eta]), "Z": np.array([0.5, -0.5, 0.5]), } path = [["\Gamma", "P", "Z", "Q", "\Gamma", "F", "P_1", "Q_1", "L", "Z"]] return {"kpoints": kpoints, "path": path} def mcl(self, b, c, beta): self.name = "MCL" eta = (1 - b * cos(beta) / c) / (2 * sin(beta) ** 2) nu = 0.5 - eta * c * cos(beta) / b kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "A": np.array([0.5, 0.5, 0.0]), "C": np.array([0.0, 0.5, 0.5]), "D": np.array([0.5, 0.0, 0.5]), "D_1": np.array([0.5, 0.5, -0.5]), "E": np.array([0.5, 0.5, 0.5]), "H": np.array([0.0, eta, 1.0 - nu]), "H_1": np.array([0.0, 1.0 - eta, nu]), "H_2": np.array([0.0, eta, -nu]), "M": np.array([0.5, eta, 1.0 - nu]), "M_1": np.array([0.5, 1 - eta, nu]), "M_2": np.array([0.5, 1 - eta, nu]), "X": np.array([0.0, 0.5, 0.0]), "Y": np.array([0.0, 0.0, 0.5]), "Y_1": np.array([0.0, 0.0, -0.5]), "Z": np.array([0.5, 0.0, 0.0]), } path = [["\Gamma", "Y", "H", "C", "E", "M_1", "A", "X", "H_1"], ["M", "D", "Z"], ["Y", "D"]] return {"kpoints": kpoints, "path": path} def mclc1(self, a, b, c, alpha): self.name = "MCLC1" zeta = (2 - b * cos(alpha) / c) / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b psi = 0.75 - a ** 2 / (4 * b ** 2 * sin(alpha) ** 2) phi = psi + (0.75 - psi) * b * cos(alpha) / c kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "N": np.array([0.5, 0.0, 0.0]), "N_1": np.array([0.0, -0.5, 0.0]), "F": np.array([1 - zeta, 1 - zeta, 1 - eta]), "F_1": np.array([zeta, zeta, eta]), "F_2": np.array([-zeta, -zeta, 1 - eta]), #'F_3': np.array([1 - zeta, -zeta, 1 - eta]), "I": np.array([phi, 1 - phi, 0.5]), "I_1": np.array([1 - phi, phi - 1, 0.5]), "L": np.array([0.5, 0.5, 0.5]), "M": np.array([0.5, 0.0, 0.5]), "X": np.array([1 - psi, psi - 1, 0.0]), "X_1": np.array([psi, 1 - psi, 0.0]), "X_2": np.array([psi - 1, -psi, 0.0]), "Y": np.array([0.5, 0.5, 0.0]), "Y_1": np.array([-0.5, -0.5, 0.0]), "Z": np.array([0.0, 0.0, 0.5]), } path = [ ["\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "F_1"], ["Y", "X_1"], ["X", "\Gamma", "N"], ["M", "\Gamma"], ] return {"kpoints": kpoints, "path": path} def mclc2(self, a, b, c, alpha): self.name = "MCLC2" zeta = (2 - b * cos(alpha) / c) / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b psi = 0.75 - a ** 2 / (4 * b ** 2 * sin(alpha) ** 2) phi = psi + (0.75 - psi) * b * cos(alpha) / c kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "N": np.array([0.5, 0.0, 0.0]), "N_1": np.array([0.0, -0.5, 0.0]), "F": np.array([1 - zeta, 1 - zeta, 1 - eta]), "F_1": np.array([zeta, zeta, eta]), "F_2": np.array([-zeta, -zeta, 1 - eta]), "F_3": np.array([1 - zeta, -zeta, 1 - eta]), "I": np.array([phi, 1 - phi, 0.5]), "I_1": np.array([1 - phi, phi - 1, 0.5]), "L": np.array([0.5, 0.5, 0.5]), "M": np.array([0.5, 0.0, 0.5]), "X": np.array([1 - psi, psi - 1, 0.0]), "X_1": np.array([psi, 1 - psi, 0.0]), "X_2": np.array([psi - 1, -psi, 0.0]), "Y": np.array([0.5, 0.5, 0.0]), "Y_1": np.array([-0.5, -0.5, 0.0]), "Z": np.array([0.0, 0.0, 0.5]), } path = [["\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "F_1"], ["N", "\Gamma", "M"]] return {"kpoints": kpoints, "path": path} def mclc3(self, a, b, c, alpha): self.name = "MCLC3" mu = (1 + b ** 2 / a ** 2) / 4.0 delta = b * c * cos(alpha) / (2 * a ** 2) zeta = mu - 0.25 + (1 - b * cos(alpha) / c) / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b phi = 1 + zeta - 2 * mu psi = eta - 2 * delta kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "F": np.array([1 - phi, 1 - phi, 1 - psi]), "F_1": np.array([phi, phi - 1, psi]), "F_2": np.array([1 - phi, -phi, 1 - psi]), "H": np.array([zeta, zeta, eta]), "H_1": np.array([1 - zeta, -zeta, 1 - eta]), "H_2": np.array([-zeta, -zeta, 1 - eta]), "I": np.array([0.5, -0.5, 0.5]), "M": np.array([0.5, 0.0, 0.5]), "N": np.array([0.5, 0.0, 0.0]), "N_1": np.array([0.0, -0.5, 0.0]), "X": np.array([0.5, -0.5, 0.0]), "Y": np.array([mu, mu, delta]), "Y_1": np.array([1 - mu, -mu, -delta]), "Y_2": np.array([-mu, -mu, -delta]), "Y_3": np.array([mu, mu - 1, delta]), "Z": np.array([0.0, 0.0, 0.5]), } path = [["\Gamma", "Y", "F", "H", "Z", "I", "F_1"], ["H_1", "Y_1", "X", "\Gamma", "N"], ["M", "\Gamma"]] return {"kpoints": kpoints, "path": path} def mclc4(self, a, b, c, alpha): self.name = "MCLC4" mu = (1 + b ** 2 / a ** 2) / 4.0 delta = b * c * cos(alpha) / (2 * a ** 2) zeta = mu - 0.25 + (1 - b * cos(alpha) / c) / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b phi = 1 + zeta - 2 * mu psi = eta - 2 * delta kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "F": np.array([1 - phi, 1 - phi, 1 - psi]), "F_1": np.array([phi, phi - 1, psi]), "F_2": np.array([1 - phi, -phi, 1 - psi]), "H": np.array([zeta, zeta, eta]), "H_1": np.array([1 - zeta, -zeta, 1 - eta]), "H_2": np.array([-zeta, -zeta, 1 - eta]), "I": np.array([0.5, -0.5, 0.5]), "M": np.array([0.5, 0.0, 0.5]), "N": np.array([0.5, 0.0, 0.0]), "N_1": np.array([0.0, -0.5, 0.0]), "X": np.array([0.5, -0.5, 0.0]), "Y": np.array([mu, mu, delta]), "Y_1": np.array([1 - mu, -mu, -delta]), "Y_2": np.array([-mu, -mu, -delta]), "Y_3": np.array([mu, mu - 1, delta]), "Z": np.array([0.0, 0.0, 0.5]), } path = [["\Gamma", "Y", "F", "H", "Z", "I"], ["H_1", "Y_1", "X", "\Gamma", "N"], ["M", "\Gamma"]] return {"kpoints": kpoints, "path": path} def mclc5(self, a, b, c, alpha): self.name = "MCLC5" zeta = (b ** 2 / a ** 2 + (1 - b * cos(alpha) / c) / sin(alpha) ** 2) / 4 eta = 0.5 + 2 * zeta * c * cos(alpha) / b mu = eta / 2 + b ** 2 / (4 * a ** 2) - b * c * cos(alpha) / (2 * a ** 2) nu = 2 * mu - zeta rho = 1 - zeta * a ** 2 / b ** 2 omega = (4 * nu - 1 - b ** 2 * sin(alpha) ** 2 / a ** 2) * c / (2 * b * cos(alpha)) delta = zeta * c * cos(alpha) / b + omega / 2 - 0.25 kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "F": np.array([nu, nu, omega]), "F_1": np.array([1 - nu, 1 - nu, 1 - omega]), "F_2": np.array([nu, nu - 1, omega]), "H": np.array([zeta, zeta, eta]), "H_1": np.array([1 - zeta, -zeta, 1 - eta]), "H_2": np.array([-zeta, -zeta, 1 - eta]), "I": np.array([rho, 1 - rho, 0.5]), "I_1": np.array([1 - rho, rho - 1, 0.5]), "L": np.array([0.5, 0.5, 0.5]), "M": np.array([0.5, 0.0, 0.5]), "N": np.array([0.5, 0.0, 0.0]), "N_1": np.array([0.0, -0.5, 0.0]), "X": np.array([0.5, -0.5, 0.0]), "Y": np.array([mu, mu, delta]), "Y_1": np.array([1 - mu, -mu, -delta]), "Y_2": np.array([-mu, -mu, -delta]), "Y_3": np.array([mu, mu - 1, delta]), "Z": np.array([0.0, 0.0, 0.5]), } path = [ ["\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "H", "F_1"], ["H_1", "Y_1", "X", "\Gamma", "N"], ["M", "\Gamma"], ] return {"kpoints": kpoints, "path": path} def tria(self): self.name = "TRI1a" kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "L": np.array([0.5, 0.5, 0.0]), "M": np.array([0.0, 0.5, 0.5]), "N": np.array([0.5, 0.0, 0.5]), "R": np.array([0.5, 0.5, 0.5]), "X": np.array([0.5, 0.0, 0.0]), "Y": np.array([0.0, 0.5, 0.0]), "Z": np.array([0.0, 0.0, 0.5]), } path = [["X", "\Gamma", "Y"], ["L", "\Gamma", "Z"], ["N", "\Gamma", "M"], ["R", "\Gamma"]] return {"kpoints": kpoints, "path": path} def trib(self): self.name = "TRI1b" kpoints = { "\Gamma": np.array([0.0, 0.0, 0.0]), "L": np.array([0.5, -0.5, 0.0]), "M": np.array([0.0, 0.0, 0.5]), "N": np.array([-0.5, -0.5, 0.5]), "R": np.array([0.0, -0.5, 0.5]), "X": np.array([0.0, -0.5, 0.0]), "Y": np.array([0.5, 0.0, 0.0]), "Z": np.array([-0.5, 0.0, 0.5]), } path = [["X", "\Gamma", "Y"], ["L", "\Gamma", "Z"], ["N", "\Gamma", "M"], ["R", "\Gamma"]] return {"kpoints": kpoints, "path": path}
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_space_group_symbol(), fitter.get_space_group_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) return finder.get_space_group_number() curr_sites = list(itertools.chain.from_iterable(disordered_sites)) min_sgnum = get_sg_info(curr_sites) logger.debug("Disordered 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 = int(ndisordered*reduce(lcm, [f.limit_denominator( ndisordered * self.max_cell_size).denominator for f in map(fractions.Fraction, index_amounts)])) # 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 generate_doc(self, dir_name, vasprun_files): """ Process aflow style runs, where each run is actually a combination of two vasp runs. """ try: fullpath = os.path.abspath(dir_name) #Defensively copy the additional fields first. This is a MUST. #Otherwise, parallel updates will see the same object and inserts #will be overridden!! d = {k: v for k, v in self.additional_fields.items()} d["dir_name"] = fullpath d["schema_version"] = VaspToDbTaskDrone.__version__ d["calculations"] = [ self.process_vasprun(dir_name, taskname, filename) for taskname, filename in vasprun_files.items()] d1 = d["calculations"][0] d2 = d["calculations"][-1] #Now map some useful info to the root level. for root_key in ["completed_at", "nsites", "unit_cell_formula", "reduced_cell_formula", "pretty_formula", "elements", "nelements", "cif", "density", "is_hubbard", "hubbards", "run_type"]: d[root_key] = d2[root_key] d["chemsys"] = "-".join(sorted(d2["elements"])) #store any overrides to the exchange correlation functional xc = d2["input"]["incar"].get("GGA") if xc: xc = xc.upper() d["input"] = {"crystal": d1["input"]["crystal"], "is_lasph": d2["input"]["incar"].get("LASPH", False), "potcar_spec": d1["input"].get("potcar_spec"), "xc_override": xc} vals = sorted(d2["reduced_cell_formula"].values()) d["anonymous_formula"] = {string.ascii_uppercase[i]: float(vals[i]) for i in range(len(vals))} d["output"] = { "crystal": d2["output"]["crystal"], "final_energy": d2["output"]["final_energy"], "final_energy_per_atom": d2["output"]["final_energy_per_atom"]} d["name"] = "aflow" p = d2["input"]["potcar_type"][0].split("_") pot_type = p[0] functional = "lda" if len(pot_type) == 1 else "_".join(p[1:]) d["pseudo_potential"] = {"functional": functional.lower(), "pot_type": pot_type.lower(), "labels": d2["input"]["potcar"]} if len(d["calculations"]) == len(self.runs) or \ list(vasprun_files.keys())[0] != "relax1": d["state"] = "successful" if d2["has_vasp_completed"] \ else "unsuccessful" else: d["state"] = "stopped" d["analysis"] = get_basic_analysis_and_error_checks(d) sg = SpacegroupAnalyzer(Structure.from_dict(d["output"]["crystal"]), 0.1) d["spacegroup"] = {"symbol": sg.get_space_group_symbol(), "number": sg.get_space_group_number(), "point_group": sg.get_point_group_symbol(), "source": "spglib", "crystal_system": sg.get_crystal_system(), "hall": sg.get_hall()} d["last_updated"] = datetime.datetime.today() return d except Exception as ex: import traceback print(traceback.format_exc()) logger.error("Error in " + os.path.abspath(dir_name) + ".\n" + traceback.format_exc()) return None