def get_kpoints_path(structure, parameters): """ Return the kpoint path information for band structure given a crystal structure, using the paths from the chosen recipe/reference. The parameters are the same as get get_path in __init__, but here all structures are input and returned as AiiDA structures rather than tuples. If you use this module, please cite the paper of the corresponding recipe (see documentation of seekpath). :param structure: The crystal structure for which we want to obtain the suggested path. It should be an AiiDA StructureData object. :param parameters: A dictionary whose key-value pairs are passed as additional kwargs to the ``seekpath.get_path`` function. :return: A dictionary with three nodes: - ``parameters``: a Dict, whose content is the same dictionary as returned by the ``seekpath.get_path`` function (see `seekpath documentation <https://seekpath.readthedocs.io/>`_), except that: - ``conv_lattice``, ``conv_positions``, ``conv_types`` are removed and replaced by the ``conv_structure`` output node - ``primitive_lattice``, ``primitive_positions``, ``primitive_types`` are removed and replaced by the ``primitive_structure`` output node - ``primitive_structure``: A StructureData with the primitive structure - ``conv_structure``: A StructureData with the primitive structure """ from aiida.tools.data.structure import spglib_tuple_to_structure, structure_to_spglib_tuple structure_tuple, kind_info, kinds = structure_to_spglib_tuple(structure) result = {} rawdict = seekpath.get_path(structure=structure_tuple, **parameters) result['parameters'] = Dict(dict=rawdict) # Replace conv structure with AiiDA StructureData conv_lattice = rawdict.pop('conv_lattice') conv_positions = rawdict.pop('conv_positions') conv_types = rawdict.pop('conv_types') result['conv_structure'] = spglib_tuple_to_structure( (conv_lattice, conv_positions, conv_types), kind_info, kinds) # Replace primitive structure with AiiDA StructureData primitive_lattice = rawdict.pop('primitive_lattice') primitive_positions = rawdict.pop('primitive_positions') primitive_types = rawdict.pop('primitive_types') result['primitive_structure'] = spglib_tuple_to_structure( (primitive_lattice, primitive_positions, primitive_types), kind_info, kinds) return result
def get_path_using_seekpath(structure, band_resolution=30): import seekpath phonopy_structure = phonopy_bulk_from_structure(structure) cell = phonopy_structure.get_cell() scaled_positions = phonopy_structure.get_scaled_positions() numbers = phonopy_structure.get_atomic_numbers() structure = (cell, scaled_positions, numbers) path_data = seekpath.get_path(structure) labels = path_data['point_coords'] band_ranges = [] for set in path_data['path']: band_ranges.append([labels[set[0]], labels[set[1]]]) bands = [] for q_start, q_end in band_ranges: band = [] for i in range(band_resolution + 1): band.append( np.array(q_start) + (np.array(q_end) - np.array(q_start)) / band_resolution * i) bands.append(band) band_structure = BandStructureData(bands=bands, labels=path_data['path'], unitcell=phonopy_structure.get_cell()) return band_structure
def get_path_using_seekpath(structure, band_resolution=30): import seekpath cell = structure.cell positions = [site.position for site in structure.sites] scaled_positions = np.dot(positions, np.linalg.inv(cell)) numbers = np.unique([site.kind_name for site in structure.sites], return_inverse=True)[1] structure2 = (cell, scaled_positions, numbers) path_data = seekpath.get_path(structure2) labels = path_data['point_coords'] band_ranges = [] for set in path_data['path']: band_ranges.append([labels[set[0]], labels[set[1]]]) bands = [] for q_start, q_end in band_ranges: band = [] for i in range(band_resolution + 1): band.append( np.array(q_start) + (np.array(q_end) - np.array(q_start)) / band_resolution * i) bands.append(band) return {'ranges': bands, 'labels': path_data['path']}
def get_path_using_seek_path(self): """ Obtain the path in reciprocal space to plot the phonon band structure :return: dictionary with list of q-points and labels of high symmetry points """ try: import seekpath cell = self._structure.get_cell() positions = self._structure.get_scaled_positions() numbers = np.unique(self._structure.get_chemical_symbols(), return_inverse=True)[1] path_data = seekpath.get_path((cell, positions, numbers)) labels = path_data['point_coords'] band_ranges = [] for set in path_data['path']: band_ranges.append([labels[set[0]], labels[set[1]]]) return {'ranges': band_ranges, 'labels': path_data['path']} except ImportError: print ('Seekpath not installed. Autopath is deactivated') band_ranges = ([[[0.0, 0.0, 0.0], [0.5, 0.0, 0.5]]]) return {'ranges': band_ranges, 'labels': [['GAMMA', '1/2 0 1/2']]}
def get_spacegroup_and_kpath(structure, symprec=1.0e-9): """ :param: structure is an instance of crystal() """ lattice = structure.cell positions = [] numbers = [] a = np.sqrt(structure.cell[0][0]**2 + structure.cell[0][1]**2 + structure.cell[0][2]**2) b = np.sqrt(structure.cell[1][0]**2 + structure.cell[1][1]**2 + structure.cell[1][2]**2) c = np.sqrt(structure.cell[2][0]**2 + structure.cell[2][1]**2 + structure.cell[2][2]**2) frac_coord = structure.get_fractional() for i in range(len(frac_coord)): positions.append( [frac_coord[i][1], frac_coord[i][2], frac_coord[i][3]]) # must be fractional coordinates numbers.append(element[frac_coord[i][0]].number) cell = (lattice, positions, numbers) return [ spglib.get_spacegroup(cell, symprec=symprec), seekpath.get_path(cell) ]
def gen_bzpath(structure): cell = (structure.lattice.matrix, structure.frac_coords, structure.atomic_numbers) path_info = seekpath.get_path(cell, True, "hpkot", 1.0e-3, 1.0e-3, 1.0) return path_info
def setUp(self): # Create trivial function object so attributes can be assigned to it NaH = type('', (), {})() cell_vec = [[0.0, 2.3995, 2.3995], [2.3995, 0.0, 2.3995], [2.3995, 2.3995, 0.0]] ion_r = [[0.5, 0.5, 0.5], [0.0, 0.0, 0.0]] ion_num = [1, 2] cell = (cell_vec, ion_r, ion_num) NaH.point_labels = seekpath.get_path(cell)["point_coords"] self.NaH = NaH
def get_band_qpoints_by_seekpath(primitive, npoints, is_const_interval=False): """q-points along BZ high symmetry paths are generated using seekpath. Parameters ---------- primitive : PhonopyAtoms Primitive cell. npoints : int Number of q-points sampled along a path including end points. is_const_interval : bool, optional When True, q-points are sampled in a similar interval. The longest path length divided by npoints including end points is used as the reference interval. Default is False. Returns ------- bands : List of ndarray Sets of qpoints that can be passed to phonopy.set_band_structure(). shape of each ndarray : (npoints, 3) labels : List of pairs of str Symbols of end points of paths. connections : List of bool This gives one path is connected to the next path, i.e., if False, there is a jump of q-points. Number of elements is the same at that of paths. """ try: import seekpath except ImportError: raise ImportError("You need to install seekpath.") band_path = seekpath.get_path(primitive.totuple()) point_coords = band_path['point_coords'] qpoints_of_paths = [] if is_const_interval: reclat = np.linalg.inv(primitive.get_cell()) else: reclat = None band_paths = [[point_coords[path[0]], point_coords[path[1]]] for path in band_path['path']] npts = _get_npts(band_paths, npoints, reclat) for c, path in enumerate(band_path['path']): q_s = np.array(point_coords[path[0]]) q_e = np.array(point_coords[path[1]]) band = [q_s + (q_e - q_s) / (npts[c] - 1) * i for i in range(npts[c])] qpoints_of_paths.append(band) labels, path_connections = _get_labels(band_path['path']) return qpoints_of_paths, labels, path_connections
def _recip_space_labels(qpts, cell=None): """ Gets q-points point labels (e.g. GAMMA, X, L) for the q-points at which the path through reciprocal space changes direction Parameters ---------- qpts : (n_qpts, 3) float ndarray The q-points to get labels for cell : (list, list, list), optional The cell structure as defined by spglib. Can be obtained by Crystal.to_spglib_cell. If not provided, the labels will be generic e.g. '1/3 1/2 0' rather than high-symmetry point labels Returns ------- labels : (n_qpts_direction_changed,) string ndarray List of the labels for each q-point at which the path through reciprocal space changes direction qpts_with_labels : (n_qpts_direction_changed,) int ndarray List of the indices of the q-points at which the path through reciprocal space changes direction """ # First and last q-points should always be labelled if len(qpts) <= 2: qpt_has_label = np.ones(len(qpts), dtype=bool) else: qpt_has_label = np.concatenate( ([True], np.logical_or(direction_changed(qpts), is_gamma(qpts[1:-1])), [True])) qpts_with_labels = np.where(qpt_has_label)[0] if cell is None: sym_label_to_coords = _generic_qpt_labels() else: try: sym_label_to_coords = seekpath.get_path(cell)["point_coords"] except SymmetryDetectionError: warnings.warn(('Could not determine cell symmetry, using generic ' 'q-point labels'), stacklevel=2) sym_label_to_coords = _generic_qpt_labels() # Get labels for each q-point labels = np.array([]) for qpt in qpts[qpts_with_labels]: labels = np.append(labels, _get_qpt_label(qpt, sym_label_to_coords)) return labels, qpts_with_labels
def test_correct_file_examples(self): """Check if these valid structures are properly parsed""" with open(os.path.join(this_folder, 'file_examples', 'list.json')) as f: list_files = json.load(f) for the_format, examples in list_files.items(): for example in examples: filename = example['file'] extra_data = example.get('extra_data', None) with open(os.path.join(this_folder, 'file_examples', filename)) as f: structure = structure_importers.get_structure_tuple( f, the_format, extra_data=extra_data) seekpath_results = seekpath.get_path(structure)
def recip_space_labels(data): """ Gets high symmetry point labels (e.g. GAMMA, X, L) for the q-points at which the path through reciprocal space changes direction Parameters ---------- data: Data object Data object containing the cell vectors, q-points and optionally ion types and coordinates (used for determining space group) Returns ------- labels : (n_qpts_direction_changed,) string ndarray List of the labels for each q-point at which the path through reciprocal space changes direction qpts_with_labels : (n_qpts_direction_changed,) int ndarray List of the indices of the q-points at which the path through reciprocal space changes direction """ # First and last q-points should always be labelled if len(data.qpts) <= 2: qpt_has_label = np.ones(len(data.qpts), dtype=bool) else: qpt_has_label = np.concatenate( ([True], direction_changed(data.qpts), [True])) qpts_with_labels = np.where(qpt_has_label)[0] # Get dict of high symmetry point labels to their coordinates for this # space group. If space group can't be determined use a generic dictionary # of fractional points sym_label_to_coords = {} if hasattr(data, 'ion_r'): _, ion_num = np.unique(data.ion_type, return_inverse=True) cell_vec = (data.cell_vec.to('angstrom')).magnitude cell = (cell_vec, data.ion_r, ion_num) sym_label_to_coords = seekpath.get_path(cell)["point_coords"] else: sym_label_to_coords = generic_qpt_labels() # Get labels for each q-point labels = np.array([]) for qpt in data.qpts[qpts_with_labels]: labels = np.append(labels, get_qpt_label(qpt, sym_label_to_coords)) return labels, qpts_with_labels
def get_kpath(self, numbers: list = [], with_time_reversal: bool = True, recipe: str = 'hpkot', threshold: float = 1e-7, symprec: float = 1e-5, angle_tolerance: float = -1) -> Tuple[Stru, Kpt]: """Obtain primitive cell and its band paths in the Brillouin zone of crystal structures. :params numbers: list of number of k points between two adjacent special k points. :params with_time_reversal: if time-reversal symmetry is present or not. Default: True :params recipe: currently only the string 'hpkot' is supported. Default: 'hpkot' :params threshold: numerical threshold. Default: 1e-7 :params symprec: distance tolerance in Cartesian coordinates to find crystal symmetry. Default: 1e-5 :params angle_tolerance: tolerance of angle between basis vectors in degrees to be tolerated in the symmetry finding. Default: -1 """ import seekpath if numbers: set_already = True else: set_already = False res = seekpath.get_path(self.spgcell, with_time_reversal, recipe, threshold, symprec, angle_tolerance) newcell = (res['primitive_lattice'], res['primitive_positions'], res['primitive_types']) stru = self.modified_stru(newcell) klabel = [] special_k = [] for k_tuple in res['path']: klabel.append(k_tuple[0]) if not set_already: numbers.append(20) klabel.append(k_tuple[1]) if not set_already: numbers.append(1) for label in klabel: special_k.append(res['point_coords'][label]) kpt = Kpt(mode='Line', numbers=numbers, special_k=special_k, klabel=klabel) return stru, kpt
def get_seekpath(cell: tuple) -> dict: """ Get seekpath results. Args: cell (tuple): Cell. Returns: dict: Labels and corresponding qpoints. """ if type(cell[2][0]) is str: numbers = get_numbers_from_symbols(cell[2]) _cell = (cell[0], cell[1], numbers) else: _cell = cell skp = seekpath.get_path(_cell) return skp
def create_k_and_symmetry_space(atoms, n_k_points=300, symprec=1e-05): spg_struct = convert_to_spg_structure(atoms) autopath = seekpath.get_path(spg_struct, symprec=symprec) path_cleaned = [] for edge in autopath['path']: edge_cleaned = [] for point in edge: if point == 'GAMMA': edge_cleaned.append('G') else: edge_cleaned.append(point.replace('_', '')) path_cleaned.append(edge_cleaned) point_coords_cleaned = {} for key in autopath['point_coords'].keys(): if key == 'GAMMA': point_coords_cleaned['G'] = autopath['point_coords'][key] else: point_coords_cleaned[key.replace( '_', '')] = autopath['point_coords'][key] density = n_k_points / 5 bandpath = atoms.cell.bandpath(path=path_cleaned, density=density, special_points=point_coords_cleaned) previous_point_position = -1. kpath = bandpath.kpts points_positions = [] points_names = [] kpoint_axis = bandpath.get_linear_kpoint_axis() for i in range(len(kpoint_axis[-2])): point_position = kpoint_axis[-2][i] point_name = kpoint_axis[-1][i] if point_position != previous_point_position: points_positions.append(point_position) points_names.append(point_name) previous_point_position = point_position points_positions = np.array(points_positions) points_positions /= points_positions.max() for i in range(len(points_names)): if points_names[i] == 'GAMMA': points_names[i] = '$\\Gamma$' return kpath, points_positions, points_names
def test_failing_file_examples(self): """These should be files that are valid in format but invalid when looking for symmetry""" with open( os.path.join(this_folder, 'file_examples_failing', 'list.json')) as f: list_files = json.load(f) for the_format, examples in list_files.items(): for example in examples: filename = example['file'] extra_data = example.get('extra_data', None) with open( os.path.join(this_folder, 'file_examples_failing', filename)) as f: structure = structure_importers.get_structure_tuple( f, the_format, extra_data=extra_data) # No additional check, just check if it works with self.assertRaises(SymmetryDetectionError): seekpath_results = seekpath.get_path(structure)
def k_path_info_with_seekpath(): """ https://seekpath.readthedocs.io/en/latest/maindoc.html#how-to-use Returns {'GAMMA': [0.0, 0.0, 0.0], 'X': [0.5, 0.0, 0.5], 'L': [0.5, 0.5, 0.5], 'W': [0.5, 0.25, 0.75], 'W_2': [0.75, 0.25, 0.5], 'K': [0.375, 0.375, 0.75], 'U': [0.625, 0.25, 0.625]} [('GAMMA', 'X'), ('X', 'U'), ('K', 'GAMMA'), ('GAMMA', 'L'), ('L', 'W'), ('W', 'X')] # Path from materials project: https://materialsproject.org/materials/mp-149/ :return: """ cell = [[0., 2.7145, 2.7145], [2.7145, 0., 2.7145], [2.7145, 2.7145, 0.]] positions = [[0.00, 0.00, 0.00], [0.25, 0.25, 0.25]] unique_indices = [14, 14] structure = (cell, positions, unique_indices) path_info = seekpath.get_path(structure, True, 'hpkot') points = path_info['point_coords'] paths = path_info['path']
def get_band_qpoints_by_seekpath(primitive, npoints): """q-points along BZ high symmetry paths are generated using seekpath. Parameters ---------- primitive : PhonopyAtoms Primitive cell. npoints : int Number of q-points sampled along a path including end points. Returns ------- bands : List of ndarray Sets of qpoints that can be passed to phonopy.set_band_structure(). shape of each ndarray : (npoints, 3) labels : List of pairs of str Symbols of end points of paths. connections : List of bool This gives one path is connected to the next path, i.e., if False, there is a jump of q-points. Number of elements is the same at that of paths. """ try: import seekpath except ImportError: raise ImportError("You need to install seekpath.") band_path = seekpath.get_path(primitive) point_coords = band_path['point_coords'] qpoints_of_paths = [] for path in band_path['path']: q_s = np.array(point_coords[path[0]]) q_e = np.array(point_coords[path[1]]) band = [q_s + (q_e - q_s) / (npoints - 1) * i for i in range(npoints)] qpoints_of_paths.append(band) labels, path_connections = _get_labels(band_path['path']) return qpoints_of_paths, labels, path_connections
def main(): parser = argparse.ArgumentParser() parser.add_argument('-f', '--file', default='POSCAR', type=str, help='path to input file') parser.add_argument('-t', '--tol', default=1e-3, type=float, help='symmetry tolerance (default 1e-3)') parser.add_argument('-o', '--output', default='poscar', help='output file format') args = parser.parse_args() struct = Structure.from_file(args.file) sym = SpacegroupAnalyzer(struct, symprec=args.tol) data = sym.get_symmetry_dataset() print("Initial structure has {} atoms".format(struct.num_sites)) print("\tSpace group number: {}".format(data['number'])) print("\tInternational symbol: {}".format(data['international'])) print("\tLattice type: {}".format(sym.get_lattice_type())) # seekpath conventional cell definition different from spglib std = spglib.refine_cell(sym._cell, symprec=args.tol) seek_data = seekpath.get_path(std) # now remake the structure lattice = seek_data['conv_lattice'] scaled_positions = seek_data['conv_positions'] numbers = seek_data['conv_types'] species = [sym._unique_species[i - 1] for i in numbers] conv = Structure(lattice, species, scaled_positions) conv.get_sorted_structure().to(filename="{}_conv".format(args.file), fmt=args.output) print("Final structure has {} atoms".format(conv.num_sites))
def __init__(self, structure, symprec=1e-3): self.structure = structure # use sym as a quick way to access the cell data sym = SpacegroupAnalyzer(structure, symprec=symprec) self._spg_data = sym.get_symmetry_dataset() # make primitive and conventional cell from seekpath output std = spglib.refine_cell(sym._cell, symprec=symprec) self._seek_data = seekpath.get_path(std) prim_lattice = self._seek_data["primitive_lattice"] prim_scaled_positions = self._seek_data["primitive_positions"] prim_numbers = self._seek_data["primitive_types"] prim_atoms = [sym._unique_species[i - 1] for i in prim_numbers] self.prim = Structure(prim_lattice, prim_atoms, prim_scaled_positions) conv_lattice = self._seek_data["conv_lattice"] conv_scaled_positions = self._seek_data["conv_positions"] conv_numbers = self._seek_data["conv_types"] conv_atoms = [sym._unique_species[i - 1] for i in conv_numbers] self.conv = Structure(conv_lattice, conv_atoms, conv_scaled_positions)
def get_path_using_seek_path(self): try: import seekpath cell = self.get_cell().T positions = self.get_scaled_positions() numbers = np.unique(self.get_atomic_elements(), return_inverse=True)[1] structure = (cell, positions, numbers) path_data = seekpath.get_path(structure) labels = path_data['point_coords'] band_ranges = [] for set in path_data['path']: band_ranges.append([labels[set[0]], labels[set[1]]]) return {'ranges': band_ranges, 'labels': path_data['path']} except ImportError: print('Seekpath not installed. Autopath is deactivated') band_ranges = ([[[0.0, 0.0, 0.0], [0.5, 0.0, 0.5]]]) return {'ranges': band_ranges, 'labels': [['GAMMA', '1/2,0,1/2']]}
def main(): parser = argparse.ArgumentParser() parser.add_argument('-f', '--file', default='POSCAR', type=str, help='path to input file') parser.add_argument('-t', '--tol', default=1e-3, type=float, help='symmetry tolerance (default 1e-3)') parser.add_argument('-o', '--output', default='poscar', help='output file format') args = parser.parse_args() struct = Structure.from_file(args.file) sym = SpacegroupAnalyzer(struct, symprec=args.tol) data = sym.get_symmetry_dataset() print('Initial structure has {} atoms'.format(struct.num_sites)) print('\tSpace group number: {}'.format(data['number'])) print('\tInternational symbol: {}'.format(data['international'])) print('\tLattice type: {}'.format(sym.get_lattice_type())) # first standardise the cell using the tolerance we want (seekpath has no # tolerance setting) std = spglib.refine_cell(sym._cell, symprec=args.tol) seek_data = seekpath.get_path(std) transform = seek_data['primitive_transformation_matrix'] # now remake the structure lattice = seek_data['primitive_lattice'] scaled_positions = seek_data['primitive_positions'] numbers = seek_data['primitive_types'] species = [sym._unique_species[i - 1] for i in numbers] prim = Structure(lattice, species, scaled_positions) prim.get_sorted_structure().to(filename='{}_prim'.format(args.file), fmt=args.output) print('Final structure has {} atoms'.format(prim.num_sites)) print('Conv -> Prim transformation matrix:') print('\t' + str(transform).replace('\n', '\n\t'))
def read_poscar(self): try: with open(self.filename, "r") as f: file = f.readlines() except FileNotFoundError: print("Not find %s"%filename) sys.exit(0) lists = [] for a in [2, 3, 4]: row = [] for b in file[a].split(): row.append(float(b)) lists.append(row) self.lattice = np.array(lists) * float(file[1]) num = 0 self.numbers = [] self.atmtyp = file[5].strip().split() self.elenu = file[6].split() for ine,a in enumerate(self.elenu): for i in range(int(a)): self.numbers.append(self.__symbol_map.get(self.atmtyp[ine])[0]) num = num + int(a) self.positions = [] for a in range(8, 8 + num): row = [] for b in file[a].split(): row.append(float(b)) self.positions.append(row) self.structure = (self.lattice, self.positions, self.numbers) hkpts_dict = seekpath.get_path(self.structure)['point_coords'] self.hkpts = np.round(np.array(list(hkpts_dict.values())), 4)
def kpath( infile=None, outfile='KPOINTS', grid_size=40, with_time_reversal=True, recipe='hpkot', threshold=1e-07, symprec=1e-05, angle_tolerence=-1.0, supercell_matrix=np.eye(3), ): """ This module creates a KPOINTS file for band structure plotting. Parameters ---------- infile : str, optional outfile : str, optional grid_size : int, optional with_time_reversal : bool, optional recepie : str, optional threshold : float, optional symprec : float, optional angle_tolerence : float, optional supercell_matrix: list, int """ welcome() file = open(infile, "r") POSCAR = file.readlines() # cell cell_matrix = POSCAR[2:5] cell = np.zeros(shape=(3, 3)) for i in range(len(cell_matrix)): cell_matrix0 = np.array(cell_matrix[i].split()) cell[i, :] = (cell_matrix0.astype(np.float)) * np.array( POSCAR[1].split()).astype(np.float) # positions # POSCAR index changed by Nicholas Pike from 5 -> 6 and from 7 -> 8 # Previously, POSCAR[5] referenced the atom names i.e. Na Cl and not the # atom numbers atoms = np.array(POSCAR[6].split()).astype(np.int) positions_matrix = POSCAR[8:8 + sum(atoms)] positions = np.zeros(shape=(np.sum(atoms), 3)) for j in range(len(positions_matrix)): positions_matrix0 = np.array(positions_matrix[j].split())[0:3] positions[j, :] = positions_matrix0.astype(np.float) # numbers numbers = np.zeros(sum(atoms)) counter = 0 atom_counter = 1 for ii in atoms: for kk in range(ii): numbers[counter] = atom_counter counter = counter + 1 atom_counter = atom_counter + 1 # seekpath structure = (cell, positions, numbers) kpath_dictionary = seekpath.get_path(structure, with_time_reversal, recipe, threshold, symprec, angle_tolerence) path_array = [""] * 2 * len(kpath_dictionary["path"]) count = 0 count2 = 1 for path_counter in kpath_dictionary["path"]: path_array[count] = path_counter[0] path_array[count2] = path_counter[1] count = count + 2 count2 = count2 + 2 coord_matrix = np.zeros(shape=(2 * len(kpath_dictionary["path"]), 3)) path_array_counter = 0 for mm in range(len(coord_matrix)): coord_matrix[mm, :] = np.dot( kpath_dictionary["point_coords"][path_array[path_array_counter]], supercell_matrix, ) path_array_counter = path_array_counter + 1 k_file = open(outfile, "w+") k_file.write("KPOINTS generated by PyProcar\n") k_file.write("%d ! Grid points\n" % grid_size) k_file.write("Line_mode\n") k_file.write("reciprocal\n") for iterator in range(len(coord_matrix)): if iterator % 2 == 0: k_file.write("%f %f %f ! %s\n" % ( coord_matrix[iterator, 0], coord_matrix[iterator, 1], coord_matrix[iterator, 2], path_array[iterator], )) else: k_file.write("%f %f %f ! %s\n\n" % ( coord_matrix[iterator, 0], coord_matrix[iterator, 1], coord_matrix[iterator, 2], path_array[iterator], )) k_file.close() return
(options, args) = parser.parse_args() structure = io.read(options.input_file) numbers = structure.get_atomic_numbers() inp = (structure.cell, structure.get_scaled_positions(), numbers) explicit_data = seekpath.get_explicit_k_path( inp, reference_distance=options.resolution) kpath = explicit_data['explicit_kpoints_rel'] weight = 0. with open(options.output_file, 'a') as outfile: for point in kpath: outfile.write("%8.6f %8.6f %8.6f %5.3f\n" % (point[0], point[1], point[2], weight)) print('Output written to {}'.format(options.output_file)) # Write new conventions cell, to ensure compliance with the kpoints new_data = seekpath.get_path(inp) new_cell = ase.Atoms(positions=new_data['conv_positions'], cell=new_data['conv_lattice'], pbc=[True, True, True]) new_cell.set_scaled_positions(new_data['conv_positions']) new_cell.set_atomic_numbers(new_data['conv_types']) print('New coordinates written to CONTCAR.conventional') io.write('CONTCAR.conventional', new_cell)
if UseSeeKpath: explicit_data = seekpath.get_explicit_k_path(inp,reference_distance=0.1) k_labels = (explicit_data['explicit_kpoints_labels']) kpoint_labels = [] for label in k_labels: label = r"$\Gamma$" if label == 'GAMMA' else label kpoint_labels.append(label) kpoint_positions = (explicit_data['explicit_kpoints_rel']) BandPath = [(tuple(pos), lab) for lab,pos in zip(kpoint_labels,kpoint_positions) if lab != ''] BandPaths = [BandPath[i:j] for i, j in zip([0]+BandPathBreaks, BandPathBreaks+[None])] if BandPaths == None: sys.exit('You need to set up the BandPath explicitly or use seeKpath to do it for you!') new_data = seekpath.get_path(inp) ReciprocalLatticeVectors = new_data['reciprocal_primitive_lattice'] #EndRegion #Region: Functions def _ReadBandEnergies(filePath): bandEnergies = []; inputReader = open(filePath, 'rU'); inputReaderCSV = csv.reader(inputReader); next(inputReaderCSV); next(inputReaderCSV);
import numpy as np import seekpath from phonopy import Phonopy from phonopy.structure.atoms import PhonopyAtoms from phonopy.interface.calculator import read_crystal_structure unitcell, _ = read_crystal_structure("POSCAR", interface_mode='vasp') cell=unitcell.get_cell() positions = unitcell.get_scaled_positions() numbers = np.unique(unitcell.get_chemical_symbols(), return_inverse=True)[1] path_data = seekpath.get_path((cell, positions, numbers)) labels = path_data['point_coords'] band_ranges = [] for set in path_data['path']: band_ranges.append([labels[set[0]], labels[set[1]]]) dict= {'ranges': band_range , 'labels': path_data['path']} with open("output.txt","w") as f : for i in range(len(dict['labels'])): f.writelines(str(dict['labels'][i])+":"+str(dict['ranges'][i]))
def get_seekpath_kpoint_path(doc, standardize=True, explicit=True, spacing=0.01, threshold=1e-7, debug=False, symmetry_tol=None): """ Return the conventional kpoint path of the relevant crystal system according to the definitions by "HKPOT" in Comp. Mat. Sci. 128, 2017: http://dx.doi.org/10.1016/j.commatsci.2016.10.015 Parameters: doc (dict/tuple): matador doc or spglib tuple to find kpoint path for. Keyword arguments: spacing (float): desired kpoint spacing threshold (float): internal seekpath threshold symmetry_tol (float): spglib symmetry tolerance Returns: dict: standardized version of input doc list: list of kpoint positions dict: full dictionary of all seekpath results """ try: from seekpath import get_explicit_k_path, get_path except ImportError: raise ImportError( "SeeK-Path dependency missing, please install it with `pip install seekpath`." ) if symmetry_tol is None: symmetry_tol = 1e-5 if isinstance(doc, tuple): spg_structure = doc else: if standardize: spg_structure = doc2spg( standardize_doc_cell(doc, symprec=symmetry_tol)) else: spg_structure = doc2spg(doc) if explicit: seekpath_results = get_explicit_k_path(spg_structure, reference_distance=spacing, with_time_reversal=True, symprec=symmetry_tol, threshold=threshold) kpt_path = seekpath_results['explicit_kpoints_rel'] else: seekpath_results = get_path(spg_structure) kpt_path = [] primitive_doc = dict() primitive_doc['lattice_cart'] = seekpath_results['primitive_lattice'] primitive_doc['positions_frac'] = seekpath_results['primitive_positions'] primitive_doc['atom_types'] = [ str(elements[i]) for i in seekpath_results['primitive_types'] ] primitive_doc['num_atoms'] = len(primitive_doc['atom_types']) primitive_doc['lattice_abc'] = cart2abc(primitive_doc['lattice_cart']) primitive_doc['cell_volume'] = cart2volume(primitive_doc['lattice_cart']) if debug: print('Found lattice type {}'.format( seekpath_results['bravais_lattice_extended'])) print('Old lattice:\n', np.asarray(doc['lattice_cart'])) print('Contained {} atoms'.format(doc['num_atoms'])) print('New lattice:\n', np.asarray(primitive_doc['lattice_cart'])) print('Contains {} atoms'.format(primitive_doc['num_atoms'])) print('k-point path contains {} points.'.format(len(kpt_path))) if 'site_occupancy' in doc: if min(doc['site_occupancy']) < 1 - EPS: print('Ignoring any site occupancy found in this cell.') primitive_doc['site_occupancy'] = [ 1 for atom in primitive_doc['atom_types'] ] return primitive_doc, kpt_path, seekpath_results
def gen_param(self, key): # Count the atoms if key == "nat": return len(self["atoms"]) # Count the atom types if key == "ntyp": return len(self["species"]) # Get a list of the species # with masses and pseudo names if key == "species": spec = [] for a in self["atoms"]: if a[0] in spec: continue spec.append(a[0]) for i, s in enumerate(spec): spec[i] = [s, elements[s]["mass number"], s + ".UPF"] return spec if key == "a": return np.linalg.norm(self["lattice"][0]) if key == "b": return np.linalg.norm(self["lattice"][1]) if key == "c": return np.linalg.norm(self["lattice"][2]) if key == "alpha": lat = self["lattice"] ret = np.dot(lat[1], lat[2]) ret = np.arccos(ret) * 180 / np.pi return ret if key == "beta": lat = self["lattice"] ret = np.dot(lat[0], lat[2]) ret = np.arccos(ret) * 180 / np.pi return ret if key == "gamma": lat = self["lattice"] ret = np.dot(lat[0], lat[1]) ret = np.arccos(ret) * 180 / np.pi return ret # Work out the space group if key == "space_group_name": self.eval_symmetry() return self["space_group_name"] # Work out the number of symmetry operations if key == "sym_ops": self.eval_symmetry() return self["sym_ops"] # Work out a good BZ path if key == "bz_path" or key == "high_symmetry_bz_points": try: import seekpath except ImportError: log("Could not import seekpath!") raise ImportError("Could not import SeeKpath!") # Convert the structure into a form that SeeKpath can digest frac_coords = [] atom_numbers = [] unique_names = [] for a in self["atoms"]: if not a[0] in unique_names: unique_names.append(a[0]) for a in self["atoms"]: frac_coords.append(a[1]) atom_numbers.append(unique_names.index(a[0])) # Call SeeKpath to get the BZ path structure = (self["lattice"], frac_coords, atom_numbers) path = seekpath.get_path(structure, with_time_reversal=True, symprec=0.001, angle_tolerance=0.5, threshold=0) # Work out how many points we have along each segment of the BZ path pc = path["point_coords"] segs = [[np.array(pc[p[0]]), np.array(pc[p[1]])] for p in path["path"]] seg_names = [[p[0], p[1]] for p in path["path"]] seg_lengths = [np.linalg.norm(s[1] - s[0]) for s in segs] tot_length = sum(seg_lengths) seg_counts = [ max(int(self["bz_path_points"] * l / tot_length), 2) for l in seg_lengths ] kpoints = [] # Will contain the k-points in the path high_symm_points = { } # Will contain the names of high-symmetry points along the path for i, c in enumerate(seg_counts): pts = np.linspace(0.0, 1.0, c) pts = [segs[i][0] + (segs[i][1] - segs[i][0]) * p for p in pts] high_symm_points[len(kpoints)] = seg_names[i][0] kpoints.extend(pts) if i + 1 < len(seg_names) and seg_names[i][1] == seg_names[ i + 1][0]: kpoints.pop() # Remove repeated high symmetry point else: high_symm_points[len(kpoints) - 1] = seg_names[i][1] self["high_symmetry_bz_points"] = high_symm_points self["bz_path"] = kpoints if key == "bz_path": return kpoints else: return high_symm_points # Find the pseudo_dir that contains # all of the needed pseudopotentials if key == "pseudo_dir": # Work out which pseudo_dirs contain # which pseudopotentials found_in = {} for s, m, p in self["species"]: found_in[p] = [] for pd in self["pseudo_dirs"]: if not os.path.isdir(pd): continue if p in os.listdir(pd): found_in[p].append(pd) # See if any one pseudo_dir contains # all of the needed pseudods for pd in self["pseudo_dirs"]: has_all = True for p in found_in: if not pd in found_in[p]: has_all = False break # This pseudo_dir contains all the # needed pseudos, go ahead and use it if has_all: return pd # See if we can combine pseudos from # multiple directories for p in found_in: if len(found_in[p]) == 0: err = "Could not find the pseudopotentail " err += p + " in any of:" for pd in self["pseudo_dirs"]: err += "\n" + pd raise ParamNotFound(err) # Create a file with the pseudopotential origin locations pof = open("pseudopotential_origin", "w") # We have found all the pseudos, collect # them into the working directory for p in found_in: # Copy the fist found pseudo to # working directory os.system("cp " + found_in[p][0] + "/" + p + " .") pof.write(p + " from " + found_in[p][0] + "\n") pof.close() return "./" # Get a dictionary of the form atom name : count if key == "atom_counts": atom_counts = defaultdict(lambda: 0) for a in self["atoms"]: if a[0] in atom_counts: atom_counts[a[0]] += 1 else: atom_counts[a[0]] = 1 return atom_counts # Reurn the stochiometry # of the given cell as a string if key == "stoichiometry_string": atom_counts = self["atom_counts"] ss = "" for a in atom_counts: ss += a + "_{0}_".format(atom_counts[a]) ss = ss[0:-1] return ss # Get an estimate for the volume of the # cell by approximating each atom as # a covalent-radius sphere if key == "covalent_volume": vol = 0.0 for a in self["atoms"]: vol += elements[a[0]]["covalent radius"]**3.0 return np.pi * vol * 4.0 / 3.0 # Default to ecutrho = 10*ecutwfc if key == "ecutrho": return 10 * self["ecutwfc"] # Generate the qpoint grid if key == "qpoint_grid": # Generate qpoint grid from spacing/min_q_grid_size rlat = np.linalg.inv(self["lattice"]).T qps = float(self["qpoint_spacing"]) b2q = lambda b: int(np.linalg.norm(b) / qps) grid = [max(self["min_q_grid_size"], b2q(b)) for b in rlat] if self["force_cube_grids"]: grid = [max(grid) for g in grid] return grid # Get individual components of qpoint grid if key == "nq1": return self["qpoint_grid"][0] if key == "nq2": return self["qpoint_grid"][1] if key == "nq3": return self["qpoint_grid"][2] # Get individial components of interpolated qpoint grid if key == "ph_interp_nq1": return self["qpoint_grid"][0] * self["ph_interp_amt"] if key == "ph_interp_nq2": return self["qpoint_grid"][0] * self["ph_interp_amt"] if key == "ph_interp_nq3": return self["qpoint_grid"][0] * self["ph_interp_amt"] # Phonon interpolation output files from prefix if key == "ph_interp_dos_file": return self["ph_interp_prefix"] + ".dos" if key == "ph_interp_freq_file": return self["ph_interp_prefix"] + ".freq" if key == "ph_interp_modes_file": return self["ph_interp_prefix"] + ".modes" if key == "ph_interp_eig_file": return self["ph_interp_prefix"] + ".eig" # Generate the kpoint grid if key == "kpoint_grid": if "kpoint_spacing" in self.par: # Generate kpoint grid from spacing rlat = np.linalg.inv(self["lattice"]).T kps = float(self["kpoint_spacing"]) b2k = lambda b: int(np.linalg.norm(b) / kps) return [b2k(b) for b in rlat] elif "kpts_per_qpt" in self.par: # Generate kpoint grid from qpoint grid kpq = self["kpts_per_qpt"] qpg = self["qpoint_grid"] return [kpq * q for q in qpg] else: msg = "Could not generate k-point grid from parameter set." raise ParamNotFound(msg) # Default to k-point parallelism if key == "pools": return self["cores_per_node"] * self["nodes"] if key == "images": return 1 # Get the default k-point grids for calculating Tc if key == "tc_kpqs": return [self["kpts_per_qpt"] - 1, self["kpts_per_qpt"]] # Default q-e bin/ path = environment path if key == "path_override": return "" # If total_walltime is > 0, this will be the time returned by # time.time() when we will run out of walltime. Otherwise is -1. if key == "end_time": if self["total_walltime"] < 0: return -1 else: return parameters.first_init_time + self["total_walltime"] # Returns the maximum seconds we let a calculation run for # from this moment in time. Is equal to the end_time minus # the current time minus the tidy_up_time. if key == "max_seconds": if self["end_time"] < 0: return 10e7 # No end time => essentially infinite time return max(self["end_time"] - time.time() - self["tidy_up_time"], 0) # This wasn't one of the generatable objects, treat # this as a KeyError, so we use the QE default value # (if there is one) exept = "Key \"{0}\" cold not be generated in parameters object." raise KeyError(exept.format(key))
def kpath(infile,grid_size,with_time_reversal,recipe,threshhold,symprec,angle_tolerence, supercell_matrix=np.eye(3)): file = open(infile,'r') POSCAR = file.readlines() #cell cell_matrix = POSCAR[2:5] cell = np.zeros(shape=(3,3)) for i in range(len(cell_matrix)): cell_matrix0= np.array(cell_matrix[i].split()) cell[i,:] = (cell_matrix0.astype(np.float))*np.array(POSCAR[1].split()).astype(np.float) #positions atoms = np.array(POSCAR[5].split()).astype(np.int) positions_matrix = POSCAR[7:7+sum(atoms)] positions = np.zeros(shape=(np.sum(atoms),3)) for j in range(len(positions_matrix)): positions_matrix0= np.array(positions_matrix[j].split())[0:3] positions[j,:] = positions_matrix0.astype(np.float) #numbers numbers = np.zeros(sum(atoms)) counter=0 atom_counter=1 for ii in atoms: for kk in range(ii): numbers[counter]=atom_counter counter=counter+1 atom_counter=atom_counter+1 #seekpath structure = (cell,positions,numbers) kpath_dictionary = seekpath.get_path(structure,with_time_reversal,recipe,threshhold,symprec,angle_tolerence) path_array = ['']*2*len(kpath_dictionary['path']) count=0 count2=1 for path_counter in kpath_dictionary['path']: path_array[count]=path_counter[0] path_array[count2]=path_counter[1] count=count+2 count2=count2+2 coord_matrix=np.zeros(shape=(2*len(kpath_dictionary['path']),3)) path_array_counter = 0 for mm in range(len(coord_matrix)): coord_matrix[mm,:] = np.dot(kpath_dictionary['point_coords'][path_array[path_array_counter]], supercell_matrix) path_array_counter=path_array_counter+1 k_file = open('KPOINTS','w+') k_file.write("KPOINTS generated by PyProcar\n") k_file.write("%d ! Grid points\n"%grid_size) k_file.write("Line_mode\n") k_file.write("reciprocal\n") for iterator in range(len(coord_matrix)): if iterator%2==0: k_file.write('%f %f %f ! %s\n'% (coord_matrix[iterator,0],coord_matrix[iterator,1],coord_matrix[iterator,2],path_array[iterator]) ) else: k_file.write('%f %f %f ! %s\n\n'% (coord_matrix[iterator,0],coord_matrix[iterator,1],coord_matrix[iterator,2],path_array[iterator]) ) k_file.close()
def reduce_to_primitive(parameters): try: import seekpath # Return a modified parameter # set with the primitive geometry frac_coords = [] atom_numbers = [] unique_names = [] for a in parameters["atoms"]: if not a[0] in unique_names: unique_names.append(a[0]) for a in parameters["atoms"]: frac_coords.append(a[1:]) atom_numbers.append(unique_names.index(a[0])) # Use seekpath to get primitive geometry structure = (parameters["lattice"], frac_coords, atom_numbers) prim_geom = seekpath.get_path( structure, with_time_reversal=True, symprec=parameters["symm_tol_cart"], angle_tolerance=parameters["symm_tol_angle"], threshold=0) # Convert back to our representation new_atoms = [] for t, f in zip(prim_geom["primitive_types"], prim_geom["primitive_positions"]): new_atoms.append([unique_names[t], f[0], f[1], f[2]]) # Overwrite new structure parameters["lattice"] = prim_geom["primitive_lattice"] parameters["atoms"] = new_atoms parameters["bz_path"] = prim_geom["path"] parameters["high_symm_points"] = prim_geom["point_coords"] # Convert k-points to cartesian coords in units of # 2pi/a0, because that's what q.e uses for some reason #recip_prim_lattice = np.array(prim_geom["reciprocal_primitive_lattice"]) #a0 = np.linalg.norm(parameters["lattice"]) #for p in parameters["high_symm_points"]: # frac_coords = parameters["high_symm_points"][p] # cart_coords = np.matmul(recip_prim_lattice.T, frac_coords) # parameters["high_symm_points"][p] = cart_coords*a0/(2*np.pi) outf = parameters["out_file"] outf.write( "Successfully reduced to primitive geometry using seekpath.\n") except ImportError: err = "Could not import seekpath =>\n" err += " 1. We cannot reduce to the primitive geometry\n" err += " 2. We cannot obtain Brilloin zone paths\n" err += " 3. 2 => we cannot calculate bandstructures\n" parameters["out_file"].write(err) if parameters["require_prim_geom"]: ex_mess = "Could not reduce to primitive geometry/find BZ path. " ex_mess += "Perhaps the version of python you're using does not " ex_mess += "have access to the seeKpath module." raise Exception(ex_mess) # Work out qpoint_grid if "qpoint_grid" in parameters: # Warn user we're using an explicit q-point grid if "qpoint_spacing" in parameters: parameters["out_file"].write( "Explicit q-point grid specified, ignoring q-point spacing\n") elif "qpoint_spacing" in parameters: # Work out qpoint_grid from qpoint_spacing parameters["qpoint_grid"] = get_kpoint_grid( parameters["lattice"], parameters["qpoint_spacing"]) # Ensure we have at least 1 q-point in each direction parameters["qpoint_grid"] = [max(1, q) for q in parameters["qpoint_grid"]] if parameters["kpoint_grid"] is None: # Work out k-point grid from q-point grid and # k-points per qpoint kpq = parameters["kpts_per_qpt"] qpg = parameters["qpoint_grid"] parameters["kpoint_grid"] = [int(q * k) for q, k in zip(qpg, kpq)] mess = "Generating k-point grid from q-point grid: {0}x{1}x{2}\n" else: # Use explicitly specified k-point grid mess = "Using explicit k-point grid: {0}x{1}x{2}\n" # Tell user the k-point grid we're using and how we got it parameters["out_file"].write(mess.format(*parameters["kpoint_grid"])) # Return resulting new parameter set return parameters
def main(): parser = argparse.ArgumentParser() parser.add_argument("-i", "--input", type=str, required=True, help="input structure file") parser.add_argument("-o", "--output", type=str, default="kpath-from-seekpath.txt", help="the output kpoitns file") parser.add_argument( "--join", type=int, default=15, help= "default number of kpoint to connect the connected high symmetry k point" ) # =============================================================================== args = parser.parse_args() structure = read_structure(filepath=args.input) lattice = structure.cell positions = [] numbers = [] a = np.sqrt(structure.cell[0][0]**2 + structure.cell[0][1]**2 + structure.cell[0][2]**2) b = np.sqrt(structure.cell[1][0]**2 + structure.cell[1][1]**2 + structure.cell[1][2]**2) c = np.sqrt(structure.cell[2][0]**2 + structure.cell[2][1]**2 + structure.cell[2][2]**2) frac_coord = structure.get_fractional() for i in range(len(frac_coord)): positions.append( [frac_coord[i][1], frac_coord[i][2], frac_coord[i][3]]) # must be fractional coordinates numbers.append(element[frac_coord[i][0]].number) cell = (lattice, positions, numbers) kpoints_seekpath = seekpath.get_path(cell) kpath = [] # [[kx, ky, kz, label, connect_indicator], ...] like [[0.0, 0.0, 0.0, 'GAMMA', 15], ...] # if connect_indicator in a kpoint is an integer, then it will connect to the following point # through the number of kpoints defined by connect_indicator. # if connect_indicator in a kpoint is '|', then it will not connect to the following point, kpath.append([ float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"][0][0]] [0]), float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"][0][0]] [1]), float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"][0][0]] [2]), kpoints_seekpath["path"][0][0], args.join, ]) kpath.append([ float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"][0][1]] [0]), float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"][0][1]] [1]), float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"][0][1]] [2]), kpoints_seekpath["path"][0][1], args. join, # first set to args.join here, and it will be reset by the following path by judge whether it is connected ]) for i in range(1, len(kpoints_seekpath["path"])): if kpoints_seekpath["path"][i][0] == kpoints_seekpath["path"][i - 1][1]: kpath[-1][4] = args.join kpath.append([ float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"] [i][1]][0]), float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"] [i][1]][1]), float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"] [i][1]][2]), kpoints_seekpath["path"][i][1], args.join, ]) else: kpath[-1][4] = "|" kpath.append([ float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"] [i][0]][0]), float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"] [i][0]][1]), float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"] [i][0]][2]), kpoints_seekpath["path"][i][0], args.join, ]) kpath.append([ float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"] [i][1]][0]), float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"] [i][1]][1]), float(kpoints_seekpath["point_coords"][kpoints_seekpath["path"] [i][1]][2]), kpoints_seekpath["path"][i][1], args.join, ]) # with open(args.output, 'w') as fout: fout.write("%d\n" % len(kpath)) for kpoint in kpath: fout.write( "%f %f %f #%s %s\n" % (kpoint[0], kpoint[1], kpoint[2], kpoint[3], str(kpoint[4]))) # print("===========================================\n") print("calculated using seekpath\n") print("===========================================\n") print("Warning:\n") print("the result is not guaranteed to be correct\n") print("-------------------------------------------\n") print("suggested k path calculated using seekpath:\n") print(kpoints_seekpath)