def kgen( filename="POSCAR", code="vasp", directory=None, make_folders=False, symprec=0.01, kpts_per_split=None, ibzkpt=None, spg=None, density=60, phonon=False, mode="bradcrack", cart_coords=False, kpt_list=None, labels=None, ): """Generate KPOINTS files for VASP band structure calculations. This script provides a wrapper around several frameworks used to generate k-points along a high-symmetry path. The paths found in Bradley and Cracknell, SeeK-path, and pymatgen are all supported. It is important to note that the standard primitive cell symmetry is different between SeeK-path and pymatgen. If the correct the structure is not used, the high-symmetry points (and band path) may be invalid. Args: filename (:obj:`str`, optional): Path to VASP structure file. Default is ``POSCAR``. code (:obj:`str`, optional): Calculation type. Default is 'vasp'; 'questaal' also supported. directory (:obj:`str`, optional): The output file directory. make_folders (:obj:`bool`, optional): Generate folders and copy in required files (INCAR, POTCAR, POSCAR, and possibly CHGCAR) from the current directory. symprec (:obj:`float`, optional): The precision used for determining the cell symmetry. kpts_per_split (:obj:`int`, optional): If set, the k-points are split into separate k-point files (or folders) each containing the number of k-points specified. This is useful for hybrid band structure calculations where it is often intractable to calculate all k-points in the same calculation. ibzkpt (:obj:`str`, optional): Path to IBZKPT file. If set, the generated k-points will be appended to the k-points in this file and given a weight of 0. This is necessary for hybrid band structure calculations. spg (:obj:`str` or :obj:`int`, optional): The space group international number or symbol to override the symmetry determined by spglib. This is not recommended and only provided for testing purposes. This option will only take effect when ``mode = 'bradcrack'``. line_density (:obj:`int`, optional): Density of k-points along the path. phonon (:obj:`bool`, optional): Write phonon q-point path instead of k-points. (Not appropriate for most codes.) mode (:obj:`str`, optional): Method used for calculating the high-symmetry path. The options are: bradcrack Use the paths from Bradley and Cracknell. See [brad]_. pymatgen Use the paths from pymatgen. See [curt]_. latimer-munro Use the paths from Latimer & Munro. See [lm]_. seekpath Use the paths from SeeK-path. See [seek]_. cart_coords (:obj:`bool`, optional): Whether the k-points are returned in cartesian or reciprocal coordinates. Defaults to ``False`` (fractional coordinates). kpt_list (:obj:`list`, optional): List of k-points to use, formatted as a list of subpaths, each containing a list of fractional k-points. For example:: [ [[0., 0., 0.], [0., 0., 0.5]], [[0.5, 0., 0.], [0.5, 0.5, 0.]] ] Will return points along ``0 0 0 -> 0 0 1/2 | 1/2 0 0 -> 1/2 1/2 0`` path_labels (:obj:`list`, optional): The k-point labels. These should be provided as a :obj:`list` of :obj:`str` for each subpath of the overall path. For example:: [ ['Gamma', 'Z'], ['X', 'M'] ] combined with the above example for ``kpt_list`` would indicate the path: Gamma -> Z | X -> M. If no labels are provided, letters from A -> Z will be used instead. If a label begins with '@' it will be concealed when plotting with sumo-bandplot. """ if code.lower() == "vasp": structure = Poscar.from_file(filename).structure elif code.lower() == "questaal": if filename.split(".")[0] == "site": site = QuestaalSite.from_file(filename) structure = site.structure alat = site.alat else: structure = QuestaalInit.from_file(filename).structure alat = None elif code.lower() == "castep": if cart_coords: logging.warning("Ignoring request for Cartesian coordinates: " "not applicable to CASTEP band structure format.") cart_coords = False if ibzkpt: logging.warning('Ignoring request to use IBZKPT ("hybrid mode"), ' "for CASTEP workflow the SCF mesh should already " "be set in input .cell file.") structure = CastepCell.from_file(filename).structure else: raise ValueError(f'Code "{code}" not recognized.') if phonon and (code.lower() not in phonon_supported_codes): logging.error("Cannot write phonon path for {code}. " "Supported codes: {supported}".format( code=code, supported=", ".join(phonon_supported_codes))) sys.exit() kpath, kpoints, labels = get_path_data( structure, mode=mode, symprec=symprec, kpt_list=kpt_list, labels=labels, spg=spg, line_density=density, cart_coords=cart_coords, ) logging.info("\nk-point label indices:") for i, label in enumerate(labels): if label: logging.info(f"\t{label}: {i + 1}") if not kpt_list and not np.allclose(structure.lattice.matrix, kpath.prim.lattice.matrix): prim_filename = f"{os.path.basename(filename)}_prim" if code.lower() == "questaal": QuestaalInit.from_structure(kpath.prim).to_file(prim_filename) elif code.lower() == "castep": CastepCell.from_structure(kpath.prim).to_file(prim_filename) else: kpath.prim.to(filename=prim_filename) logging.error( "\nWARNING: The input structure does not match the " "expected standard\nprimitive symmetry, the path may be " "incorrect! Use at your own risk.\n\nThe correct " "symmetry primitive structure has been saved as {}.".format( prim_filename)) ibz = _parse_ibzkpt(ibzkpt) if ibz and kpts_per_split is None: logging.info("\nFound {} total kpoints in path, do you want to " "split them up? (y/n)".format(len(kpoints))) if input()[0].lower() == "y": logging.info("How many kpoints per file?") kpts_per_split = int(input()) if code.lower() == "vasp": sumo.io.vasp.write_kpoint_files( filename, kpoints, labels, make_folders=make_folders, ibzkpt=ibz, kpts_per_split=kpts_per_split, directory=directory, cart_coords=cart_coords, ) elif code.lower() == "castep": sumo.io.castep.write_kpoint_files( filename, kpoints, labels, make_folders=make_folders, kpts_per_split=kpts_per_split, phonon=phonon, directory=directory, ) elif code.lower() == "questaal": if cart_coords: kpoints = [kpoint / (2 * np.pi) for kpoint in kpoints] if alat is not None: logging.info( f"Multiplying kpoint values by ALAT = {alat} Bohr") _bohr_to_angstrom = 0.5291772 kpoints = [ kpoint * alat * _bohr_to_angstrom for kpoint in kpoints ] sumo.io.questaal.write_kpoint_files( filename, kpoints, labels, make_folders=make_folders, directory=directory, cart_coords=cart_coords, )
def _bs_from_filename( filename, poscar, dim, symprec, spg, kpt_list, labels, primitive_axis, units, born, mode, eigenvectors, line_density, ): """Analyse input files to create band structure""" if ".yaml" in filename: logging.warning( f"Reading pre-computed band structure from {filename}. " "Be aware that many phonon-bandplot options will not " "be relevant.") yaml_file = filename phonon = None try: poscar = poscar if poscar else "POSCAR" poscar = Poscar.from_file(poscar) except OSError: msg = "Cannot find POSCAR file, cannot generate symmetry path." logging.error(f"\n {msg}") sys.exit() elif ("FORCE_SETS" in filename or "FORCE_CONSTANTS" in filename or ".hdf5" in filename): try: poscar = poscar if poscar else "POSCAR" poscar = Poscar.from_file(poscar) except OSError: msg = "Cannot find POSCAR file, cannot generate symmetry path." logging.error(f"\n {msg}") sys.exit() if not dim: logging.info("Supercell size (--dim option) not provided.\n" "Attempting to guess supercell dimensions.\n") try: sposcar = Poscar.from_file("SPOSCAR") except OSError: msg = "Could not determine supercell size (use --dim flag)." logging.error(f"\n {msg}") sys.exit() dim = sposcar.structure.lattice.matrix @ poscar.structure.lattice.inv_matrix # round due to numerical noise error dim = np.around(dim, 5) elif len(dim) == 9: dim = np.array(dim).reshape(3, 3) elif np.array(dim).shape != (3, 3): dim = np.diagflat(dim) logging.info("Using supercell with dimensions:") logging.info("\t" + str(dim).replace("\n", "\n\t") + "\n") factors = { "ev": VaspToEv, "thz": VaspToTHz, "mev": VaspToEv * 1000, "cm-1": VaspToCm, } phonon = load_phonopy( filename, poscar.structure, dim, primitive_matrix=primitive_axis, factor=factors[units.lower()], symmetrise=True, born=born, write_fc=False, ) elif ".phonon" in filename: logging.warning("Reading pre-computed band structure from CASTEP. " "Be aware that many phonon-bandplot options will not " "be relevant.") castep_phonon = CastepPhonon.from_file(filename) cell_file = ".".join(filename.split(".")[:-1] + ["cell"]) if isfile(cell_file): logging.info( f"Found .cell file, reading x-axis labels from {cell_file}") castep_phonon.set_labels_from_file(cell_file) else: logging.warning("No .cell file found, cannot read x-axis labels.") factors = { "cm-1": 1.0, "thz": 1 / THzToCm, "ev": CmToEv, "mev": 1000 * CmToEv } castep_phonon.frequencies *= factors[units.lower()] bs = castep_phonon.get_band_structure() return bs, None else: msg = f"Do not recognise file type of {filename}" logging.error(f"\n {msg}") sys.exit() # calculate band structure kpath, kpoints, labels = get_path_data( poscar.structure, mode=mode, symprec=symprec, spg=spg, kpt_list=kpt_list, labels=labels, phonopy=True, line_density=line_density, ) # todo: calculate dos and plot also # phonon.set_mesh(mesh, is_gamma_center=False, is_eigenvectors=True, # is_mesh_symmetry=False) # phonon.set_partial_DOS() if ".yaml" not in filename: phonon.run_band_structure(kpoints, with_eigenvectors=eigenvectors, labels=labels) yaml_file = "sumo_band.yaml" phonon.band_structure.write_yaml(filename=yaml_file) bs = get_ph_bs_symm_line(yaml_file, has_nac=False, labels_dict=kpath.kpoints) return bs, phonon
def _bs_from_filename(filename, poscar, dim, symprec, spg, kpt_list, labels, primitive_axis, units, born, mode, eigenvectors): """Analyse input files to create band structure""" if '.yaml' in filename: yaml_file = filename elif ('FORCE_SETS' in filename or 'FORCE_CONSTANTS' in filename or '.hdf5' in filename): try: poscar = poscar if poscar else 'POSCAR' poscar = Poscar.from_file(poscar) except IOError: msg = "Cannot find POSCAR file, cannot generate symmetry path." logging.error("\n {}".format(msg)) sys.exit() if not dim: logging.info("Supercell size (--dim option) not provided.\n" "Attempting to guess supercell dimensions.\n") try: sposcar = Poscar.from_file("SPOSCAR") except IOError: msg = "Could not determine supercell size (use --dim flag)." logging.error("\n {}".format(msg)) sys.exit() dim = (sposcar.structure.lattice.matrix * poscar.structure.lattice.inv_matrix) # round due to numerical noise error dim = np.around(dim, 5) elif len(dim) == 9: dim = np.array(dim).reshape(3, 3) elif np.array(dim).shape != (3, 3): dim = np.diagflat(dim) logging.info("Using supercell with dimensions:") logging.info('\t' + str(dim).replace('\n', '\n\t') + '\n') factors = { 'ev': VaspToEv, 'thz': VaspToTHz, 'mev': VaspToEv * 1000, 'cm-1': VaspToCm } phonon = load_phonopy(filename, poscar.structure, dim, symprec=symprec, primitive_matrix=primitive_axis, factor=factors[units.lower()], symmetrise=True, born=born, write_fc=False) elif '.phonon' in filename: logging.warning("Reading pre-computed band structure from CASTEP. " "Be aware that many phonon-bandplot options will not " "be relevant.") castep_phonon = CastepPhonon.from_file(filename) cell_file = '.'.join(filename.split('.')[:-1] + ['cell']) if isfile(cell_file): logging.info("Found .cell file, reading x-axis labels from " "{}".format(cell_file)) castep_phonon.set_labels_from_file(cell_file) else: logging.warning("No .cell file found, cannot read x-axis labels.") factors = { 'cm-1': 1., 'thz': 1 / THzToCm, 'ev': CmToEv, 'mev': 1000 * CmToEv } castep_phonon.frequencies *= factors[units.lower()] bs = castep_phonon.get_band_structure() return bs, None else: msg = "Do not recognise file type of {}".format(filename) logging.error("\n {}".format(msg)) sys.exit() # calculate band structure kpath, kpoints, labels = get_path_data(poscar.structure, mode=mode, symprec=symprec, spg=spg, kpt_list=kpt_list, labels=labels, phonopy=True) # todo: calculate dos and plot also # phonon.set_mesh(mesh, is_gamma_center=False, is_eigenvectors=True, # is_mesh_symmetry=False) # phonon.set_partial_DOS() phonon.set_band_structure(kpoints, is_eigenvectors=eigenvectors, labels=labels) yaml_file = 'sumo_band.yaml' phonon._band_structure.write_yaml(filename=yaml_file) bs = get_ph_bs_symm_line(yaml_file, has_nac=False, labels_dict=kpath.kpoints) return bs, phonon
def phonon_bandplot(filename, poscar=None, prefix=None, directory=None, dim=None, born=None, qmesh=None, spg=None, primitive_axis=None, line_density=60, symprec=0.01, mode='bradcrack', kpt_list=None, eigenvectors=False, labels=None, height=6., width=6., style=None, no_base_style=False, ymin=None, ymax=None, image_format='pdf', dpi=400, plt=None, fonts=None, dos=None): """A script to plot phonon band structure diagrams. Args: filename (str): Path to phonopy output. Can be a band structure yaml file, ``FORCE_SETS``, ``FORCE_CONSTANTS``, or ``force_constants.hdf5``. poscar (:obj:`str`, optional): Path to POSCAR file of unitcell. Not required if plotting the phonon band structure from a yaml file. If not specified, the script will search for a POSCAR file in the current directory. prefIx (:obj:`str`, optional): Prefix for file names. directory (:obj:`str`, optional): The directory in which to save files. born (:obj:`str`, optional): Path to file containing Born effective charges. Should be in the same format as the file produced by the ``phonopy-vasp-born`` script provided by phonopy. qmesh (:obj:`list` of :obj:`int`, optional): Q-point mesh to use for calculating the density of state. Formatted as a 3x1 :obj:`list` of :obj:`int`. spg (:obj:`str` or :obj:`int`, optional): The space group international number or symbol to override the symmetry determined by spglib. This is not recommended and only provided for testing purposes. This option will only take effect when ``mode = 'bradcrack'``. primitive_matrix (:obj:`list`, optional): The transformation matrix from the conventional to primitive cell. Only required when the conventional cell was used as the starting structure. Should be provided as a 3x3 :obj:`list` of :obj:`float`. line_density (:obj:`int`, optional): Density of k-points along the path. mode (:obj:`str`, optional): Method used for calculating the high-symmetry path. The options are: bradcrack Use the paths from Bradley and Cracknell. See [brad]_. pymatgen Use the paths from pymatgen. See [curt]_. seekpath Use the paths from SeeK-path. See [seek]_. kpt_list (:obj:`list`, optional): List of k-points to use, formatted as a list of subpaths, each containing a list of fractional k-points. For example:: [ [[0., 0., 0.], [0., 0., 0.5]], [[0.5, 0., 0.], [0.5, 0.5, 0.]] ] Will return points along ``0 0 0 -> 0 0 1/2 | 1/2 0 0 -> 1/2 1/2 0`` path_labels (:obj:`list`, optional): The k-point labels. These should be provided as a :obj:`list` of :obj:`str` for each subpath of the overall path. For example:: [ ['Gamma', 'Z'], ['X', 'M'] ] combined with the above example for ``kpt_list`` would indicate the path: Gamma -> Z | X -> M. If no labels are provided, letters from A -> Z will be used instead. eigenvectors (:obj:`bool`, optional): Write the eigenvectors to the yaml file. dos (str): Path to Phonopy total dos .dat file height (:obj:`float`, optional): The height of the plot. width (:obj:`float`, optional): The width of the plot. ymin (:obj:`float`, optional): The minimum energy on the y-axis. ymax (:obj:`float`, optional): The maximum energy on the y-axis. style (:obj:`list` or :obj:`str`, optional): (List of) matplotlib style specifications, to be composed on top of Sumo base style. no_base_style (:obj:`bool`, optional): Prevent use of sumo base style. This can make alternative styles behave more predictably. image_format (:obj:`str`, optional): The image file format. Can be any format supported by matplotlib, including: png, jpg, pdf, and svg. Defaults to pdf. dpi (:obj:`int`, optional): The dots-per-inch (pixel density) for the image. plt (:obj:`matplotlib.pyplot`, optional): A :obj:`matplotlib.pyplot` object to use for plotting. fonts (:obj:`list`, optional): Fonts to use in the plot. Can be a a single font, specified as a :obj:`str`, or several fonts, specified as a :obj:`list` of :obj:`str`. Returns: A matplotlib pyplot object. """ if '.yaml' in filename: yaml_file = filename elif ('FORCE_SETS' == filename or 'FORCE_CONSTANTS' == filename or '.hdf5' in filename): try: poscar = poscar if poscar else 'POSCAR' poscar = Poscar.from_file(poscar) except IOError: msg = "Cannot find POSCAR file, cannot generate symmetry path." logging.error("\n {}".format(msg)) sys.exit() if not dim: logging.info("Supercell size (--dim option) not provided.\n" "Attempting to guess supercell dimensions.\n") try: sposcar = Poscar.from_file("SPOSCAR") except IOError: msg = "Could not determine supercell size (use --dim flag)." logging.error("\n {}".format(msg)) sys.exit() dim = (sposcar.structure.lattice.matrix * poscar.structure.lattice.inv_matrix) # round due to numerical noise error dim = np.around(dim, 5) elif np.array(dim).shape != (3, 3): dim = np.diagflat(dim) logging.info("Using supercell with dimensions:") logging.info('\t' + str(dim).replace('\n', '\n\t') + '\n') phonon = load_phonopy(filename, poscar.structure, dim, symprec=symprec, primitive_matrix=primitive_axis, factor=VaspToTHz, symmetrise=True, born=born, write_fc=False) # calculate band structure kpath, kpoints, labels = get_path_data(poscar.structure, mode=mode, symprec=symprec, spg=spg, kpt_list=kpt_list, labels=labels, phonopy=True) # todo: calculate dos and plot also # phonon.set_mesh(mesh, is_gamma_center=False, is_eigenvectors=True, # is_mesh_symmetry=False) # phonon.set_partial_DOS() phonon.set_band_structure(kpoints, is_eigenvectors=eigenvectors) yaml_file = 'sumo_band.yaml' phonon._band_structure.write_yaml(labels=labels, filename=yaml_file) else: msg = "Do not recognise file type of {}".format(filename) logging.error("\n {}".format(msg)) sys.exit() save_files = False if plt else True # don't save if pyplot object provided bs = get_ph_bs_symm_line(yaml_file, has_nac=False, labels_dict=kpath.kpoints) # Replace dos filename with data array if dos is not None: if isfile(dos): dos = np.genfromtxt(dos, comments='#') elif dos: phonon.set_mesh(qmesh, is_gamma_center=False, is_eigenvectors=True, is_mesh_symmetry=False) phonon.set_total_DOS() dos_freq, dos_val = phonon.get_total_DOS() dos = np.zeros((len(dos_freq), 2)) dos[:, 0], dos[:, 1] = dos_freq, dos_val plotter = SPhononBSPlotter(bs) plt = plotter.get_plot(ymin=ymin, ymax=ymax, height=height, width=width, plt=plt, fonts=fonts, dos=dos) if save_files: basename = 'phonon_band.{}'.format(image_format) filename = '{}_{}'.format(prefix, basename) if prefix else basename if directory: filename = os.path.join(directory, filename) if dpi is None: dpi = rcParams['figure.dpi'] plt.savefig(filename, format=image_format, dpi=dpi, bbox_inches='tight') filename = save_data_files(bs, prefix=prefix, directory=directory) else: return plt
def kgen(filename='POSCAR', directory=None, make_folders=False, symprec=0.01, kpts_per_split=None, ibzkpt=None, spg=None, density=60, mode='bradcrack', cart_coords=False, kpt_list=None, labels=None): """Generate KPOINTS files for VASP band structure calculations. This script provides a wrapper around several frameworks used to generate k-points along a high-symmetry path. The paths found in Bradley and Cracknell, SeeK-path, and pymatgen are all supported. It is important to note that the standard primitive cell symmetry is different between SeeK-path and pymatgen. If the correct the structure is not used, the high-symmetry points (and band path) may be invalid. Args: filename (:obj:`str`, optional): Path to VASP structure file. Default is ``POSCAR``. directory (:obj:`str`, optional): The output file directory. make_folders (:obj:`bool`, optional): Generate folders and copy in required files (INCAR, POTCAR, POSCAR, and possibly CHGCAR) from the current directory. symprec (:obj:`float`, optional): The precision used for determining the cell symmetry. kpts_per_split (:obj:`int`, optional): If set, the k-points are split into separate k-point files (or folders) each containing the number of k-points specified. This is useful for hybrid band structure calculations where it is often intractable to calculate all k-points in the same calculation. ibzkpt (:obj:`str`, optional): Path to IBZKPT file. If set, the generated k-points will be appended to the k-points in this file and given a weight of 0. This is necessary for hybrid band structure calculations. spg (:obj:`str` or :obj:`int`, optional): The space group international number or symbol to override the symmetry determined by spglib. This is not recommended and only provided for testing purposes. This option will only take effect when ``mode = 'bradcrack'``. line_density (:obj:`int`, optional): Density of k-points along the path. mode (:obj:`str`, optional): Method used for calculating the high-symmetry path. The options are: bradcrack Use the paths from Bradley and Cracknell. See [brad]_. pymatgen Use the paths from pymatgen. See [curt]_. seekpath Use the paths from SeeK-path. See [seek]_. cart_coords (:obj:`bool`, optional): Whether the k-points are returned in cartesian or reciprocal coordinates. Defaults to ``False`` (fractional coordinates). kpt_list (:obj:`list`, optional): List of k-points to use, formatted as a list of subpaths, each containing a list of fractional k-points. For example:: [ [[0., 0., 0.], [0., 0., 0.5]], [[0.5, 0., 0.], [0.5, 0.5, 0.]] ] Will return points along ``0 0 0 -> 0 0 1/2 | 1/2 0 0 -> 1/2 1/2 0`` path_labels (:obj:`list`, optional): The k-point labels. These should be provided as a :obj:`list` of :obj:`str` for each subpath of the overall path. For example:: [ ['Gamma', 'Z'], ['X', 'M'] ] combined with the above example for ``kpt_list`` would indicate the path: Gamma -> Z | X -> M. If no labels are provided, letters from A -> Z will be used instead. """ poscar = Poscar.from_file(filename) kpath, kpoints, labels = get_path_data(poscar.structure, mode=mode, symprec=symprec, kpt_list=kpt_list, labels=labels, spg=spg, line_density=density) logging.info('\nk-point label indices:') for i, label in enumerate(labels): if label: logging.info('\t{}: {}'.format(label, i + 1)) if not kpt_list and not np.allclose(poscar.structure.lattice.matrix, kpath.prim.lattice.matrix): prim_filename = '{}_prim'.format(os.path.basename(filename)) kpath.prim.to(filename=prim_filename) logging.error( "\nWARNING: The input structure does not match the " "expected standard\nprimitive symmetry, the path may be " "incorrect! Use at your own risk.\n\nThe correct " "symmetry primitive structure has been saved as {}.".format( prim_filename)) ibz = _parse_ibzkpt(ibzkpt) if make_folders and ibz and kpts_per_split is None: logging.info("\nFound {} total kpoints in path, do you want to " "split them up? (y/n)".format(len(kpoints))) if input()[0].lower() == 'y': logging.info("How many kpoints per file?") kpts_per_split = int(input()) write_kpoint_files(filename, kpoints, labels, make_folders=make_folders, ibzkpt=ibz, kpts_per_split=kpts_per_split, directory=directory, cart_coords=cart_coords)