def test_partial_disorder(self): s = Structure.from_file(filename=os.path.join(test_dir, "garnet.cif")) a = SpacegroupAnalyzer(s, 0.1) prim = a.find_primitive() s = prim.copy() s["Al3+"] = {"Al3+": 0.5, "Ga3+": 0.5} adaptor = EnumlibAdaptor(s, 1, 1, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 7) for s in structures: self.assertEqual(s.formula, 'Ca12 Al4 Ga4 Si12 O48') s = prim.copy() s["Ca2+"] = {"Ca2+": 1/3, "Mg2+": 2/3} adaptor = EnumlibAdaptor(s, 1, 1, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 20) for s in structures: self.assertEqual(s.formula, 'Ca4 Mg8 Al8 Si12 O48') s = prim.copy() s["Si4+"] = {"Si4+": 1/3, "Ge4+": 2/3} adaptor = EnumlibAdaptor(s, 1, 1, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 18) for s in structures: self.assertEqual(s.formula, 'Ca12 Al8 Si4 Ge8 O48')
def 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_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 calc_shiftk(self, symprec=0.01, angle_tolerance=5): """ Find the values of `shiftk` and `nshiftk` appropriated for the sampling of the Brillouin zone. When the primitive vectors of the lattice do NOT form a FCC or a BCC lattice, the usual (shifted) Monkhorst-Pack grids are formed by using nshiftk=1 and shiftk 0.5 0.5 0.5 . This is often the preferred k point sampling. For a non-shifted Monkhorst-Pack grid, use `nshiftk=1` and `shiftk 0.0 0.0 0.0`, but there is little reason to do that. When the primitive vectors of the lattice form a FCC lattice, with rprim:: 0.0 0.5 0.5 0.5 0.0 0.5 0.5 0.5 0.0 the (very efficient) usual Monkhorst-Pack sampling will be generated by using nshiftk= 4 and shiftk:: 0.5 0.5 0.5 0.5 0.0 0.0 0.0 0.5 0.0 0.0 0.0 0.5 When the primitive vectors of the lattice form a BCC lattice, with rprim:: -0.5 0.5 0.5 0.5 -0.5 0.5 0.5 0.5 -0.5 the usual Monkhorst-Pack sampling will be generated by using nshiftk= 2 and shiftk:: 0.25 0.25 0.25 -0.25 -0.25 -0.25 However, the simple sampling nshiftk=1 and shiftk 0.5 0.5 0.5 is excellent. For hexagonal lattices with hexagonal axes, e.g. rprim:: 1.0 0.0 0.0 -0.5 sqrt(3)/2 0.0 0.0 0.0 1.0 one can use nshiftk= 1 and shiftk 0.0 0.0 0.5 In rhombohedral axes, e.g. using angdeg 3*60., this corresponds to shiftk 0.5 0.5 0.5, to keep the shift along the symmetry axis. Returns: Suggested value of shiftk. """ # Find lattice type. sym = SpacegroupAnalyzer(self, symprec=symprec, angle_tolerance=angle_tolerance) lattice_type, spg_symbol = sym.get_lattice_type(), sym.get_spacegroup_symbol() # Check if the cell is primitive is_primitve = len(sym.find_primitive()) == len(self) # Generate the appropriate set of shifts. shiftk = None if is_primitve: if lattice_type == "cubic": if "F" in spg_symbol: # FCC shiftk = [0.5, 0.5, 0.5, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5] elif "I" in spg_symbol: # BCC shiftk = [0.25, 0.25, 0.25, -0.25, -0.25, -0.25] #shiftk = [0.5, 0.5, 05]) elif lattice_type == "hexagonal": # Find the hexagonal axis and set the shift along it. for i, angle in enumerate(self.lattice.angles): if abs(angle - 120) < 1.0: j = (i + 1) % 3 k = (i + 2) % 3 hex_ax = [ax for ax in range(3) if ax not in [j,k]][0] break else: raise ValueError("Cannot find hexagonal axis") shiftk = [0.0, 0.0, 0.0] shiftk[hex_ax] = 0.5 elif lattice_type == "tetragonal": if "I" in spg_symbol: # BCT shiftk = [0.25, 0.25, 0.25, -0.25, -0.25, -0.25] if shiftk is None: # Use default value. shiftk = [0.5, 0.5, 0.5] return np.reshape(shiftk, (-1,3))
class SpaceGroup(object): def __init__(self, configfile, symprec=0.01, angle_trelance=6): self.config_obj = ParseConfig(configfile) self.symprec = symprec self.angle_trelance = angle_trelance self.main() def set_structure(self): # # ======= ATOMIC POSITIONS ======= # a, b, c = self.config_obj.lattice_length[:3] alpha, beta, gamma = self.config_obj.lattice_angle[:3] lattice = mg.Lattice.from_parameters(a, b, c, alpha, beta, gamma) self.atoms = self.config_obj.atom_list atomic_positions = self.config_obj.atomic_position self.structure = mg.Structure(lattice, self.atoms, atomic_positions) def main(self): self.set_structure() # # generate instance for space group # self.spg = SpacegroupAnalyzer(self.structure, symprec=self.symprec, angle_tolerance=self.angle_trelance) try: self.hmname = self.spg.get_space_group_symbol() self.ispg = self.spg.get_space_group_number() except TypeError: print() print(' **** INTERNAL LIBRARY WARNING ****') print(' pymatgen and spglib cannot identify space group', end='') print(' for smaller primitive cell.') print(' we expect this bug will be fixed near future.', end='') print('metis uses original primitive cell.') self.spg = None self.hmname = None self.ispg = None def generate_primitive_lattice(self): # # in this case, spglib cannot identify space group # if self.spg is None: return my_data = str(self.spg.find_primitive()) data_region = False atomic_positions = [] for line in my_data.split('\n'): linebuf = line.strip() if re.search('^abc\s+:', linebuf): lattice_length = \ [float(x) for x in linebuf.split(':')[1].split()] if re.search('^angles:', linebuf): lattice_angle = \ [float(x) for x in linebuf.split(':')[1].split()] if re.search('^#', linebuf) and 'SP' in linebuf: data_region = True continue if data_region: if re.search('^---', linebuf): continue element = linebuf.split()[1] pos = [float(x) for x in linebuf.split()[2:]] info = {'element': element, 'position': pos} atomic_positions.append(info) lattice_type = self.hmname[0] compound_name = self.get_compound_name(atomic_positions) return {'ispg': self.ispg, 'hmname': self.hmname, 'lattice_length': lattice_length, 'lattice_angle': lattice_angle, 'lattice_type': lattice_type, 'atomic_positions': atomic_positions, 'compound_name': compound_name} def get_compound_name(self, atomic_positions): natoms = None compound = '' pre_element = None for atom in atomic_positions: element = atom['element'] if pre_element != element: if pre_element is not None: compound += str(natoms) compound += element natoms = 1 pre_element = element else: natoms += 1 compound += str(natoms) return compound def show_info(self): print('----- symmetrized structure(conventional unit cell) -----') print(self.spg.get_symmetrized_structure()) print() print('----- primitive structure -----') print(self.spg.find_primitive()) print() # # name of space group # print('--- infomation of space group ---') print(' HM symbol: {} '.format(self.hmname)) print(' Space Group number = #{}'.format(self.ispg)) print(' point group : {}'.format(self.spg.get_point_group_symbol())) print() print() print('--- crystal system ---') print(' crystal system:{}'.format(self.spg.get_crystal_system())) print(' lattice type:{}'.format(self.spg.get_lattice_type())) print() # # total data set # dataset = self.spg.get_symmetry_dataset() wyckoff_position = dataset['wyckoffs'] atom_pos = [] atom_name = [] wyckoff_data = [] for (i, wyckoff) in enumerate(wyckoff_position): aname = self.atoms[i] if aname not in atom_name or wyckoff not in atom_pos: info = {'site_type_symbol': aname, 'wyckoff_letter': wyckoff, 'multiply': 0} wyckoff_data.append(info) atom_pos.append(wyckoff) atom_name.append(aname) for (i, wyckoff) in enumerate(wyckoff_position): aname = self.atoms[i] for info in wyckoff_data: if info['site_type_symbol'] == aname: if info['wyckoff_letter'] == wyckoff: info['multiply'] += 1 print(' # of kinds of atoms = {}'.format(len(wyckoff_data))) print() for info in wyckoff_data: site_name = '{0}{1}-site'.format(info['multiply'], info['wyckoff_letter']) atom_name = info['site_type_symbol'] print(' atom:{0} wyckoff: {1}'.format(atom_name, site_name)) return def get_conventional_cell(self): # # infomation of original primitive cell # self.prim_atom_info = {} data_set = self.spg.get_symmetry_dataset() wyckoffs = data_set['wyckoffs'] natoms = len(self.atoms) atom_info_tmp = [] for iatom in range(natoms): element = self.atoms[iatom], wyckoff_letter = wyckoffs[iatom] info = {'element': self.atoms[iatom], 'wyckoff_letter': wyckoffs[iatom]} atom_info_tmp.append(info) element_list = [] atom_info = [] for info in atom_info_tmp: element = info['element'] wyckoff_letter = info['wyckoff_letter'] if element not in element_list: element_list.append(element) info = {} info['element'] = element info['wyckoff_letter'] = [wyckoff_letter] atom_info.append(info) else: atom_info[-1]['wyckoff_letter'].append(wyckoff_letter) self.primitive_cell_info = {'ispg': self.ispg, 'natoms': natoms, 'atom_info': atom_info} # # conventional unit cell # data = str(self.spg.get_conventional_standard_structure()) data_set = self.spg.get_symmetry_dataset() wyckoffs = data_set['wyckoffs'] data_region = False atom_info = [] self.atoms = [] atomic_positions = [] for line in data.split('\n'): linebuf = line.strip() if re.search('abc\s+:', linebuf): lattice_length = \ [float(x) for x in linebuf.split(':')[1].split()] if re.search('angles\s*:', linebuf): lattice_angle = \ [float(x) for x in linebuf.split(':')[1].split()] if re.search('^#\s+SP', linebuf): data_region = True continue if data_region: if re.search('^---', linebuf): continue data_line = linebuf.split() element = data_line[1] position = [float(x) for x in data_line[2:]] self.atoms.append(element) atomic_positions.append(position) # # generate conventional infomation # a, b, c = lattice_length alpha, beta, gamma = lattice_angle lattice = mg.Lattice.from_parameters(a, b, c, alpha, beta, gamma) try: self.structure = mg.Structure(lattice, self.atoms, atomic_positions) self.spg = SpacegroupAnalyzer(self.structure, symprec=self.symprec, angle_tolerance=self.angle_trelance) self.ispg = self.spg.get_space_group_number() self.hmname = self.spg.get_space_group_symbol() except TypeError: print() print(' **** INTERNAL LIBRARY WARNING ****') print(' pymatgen and spglib cannot identify space group', end='') print(' for smaller primitive cell.') print(' we expect this bug will be fixed near future.', end='') print(' metis uses original primitive cell.') self.spg = None self.ispg = None self.hmname = None return self def symmetrized(self): self.get_symmetrized_structure()