def get_kpath(self): """Returns the special path in k-space for the seed configuration of this group. Returns: tuple: special path in k-space. First term is a list of special point labels; second is the list of points corresponding to those labels. """ if self._kpath is None: from matdb.kpoints import parsed_kpath self._kpath = parsed_kpath(self.atoms) return self._kpath
def test_kpath(): """Performs a simple test to extract the kpath for an alloy. """ from matdb.atoms import Atoms from matdb.kpoints import parsed_kpath from matdb.utility import relpath filepath = relpath("tests/files/POSCAR-AgPd-50") at0 = Atoms(filepath, format="vasp") model = { 'band': [[0.0, 0.0, 0.0], [0.0, 0.0, 0.5], [0.25, 0.25, 0.25], [0.0, 0.5, 0.0], [0.0, 0.0, 0.0], [0.5, 0.5, -0.5], [0.3126058432665583, 0.6873941567334416, -0.3126058432665583], [0.0, 0.0, 0.0], [-0.12521168653311662, 0.12521168653311662, 0.5], [0.5, 0.5, -0.5]], 'labels': [ '\\Gamma', 'X', 'P', 'N', '\\Gamma', 'M', 'S|S_0', '\\Gamma|X', 'R|G', 'M' ] } labels, band = parsed_kpath(at0) assert labels == model["labels"] assert band == model["band"]
def band_plot(dbs, fits=None, npts=100, title="{} Phonon Spectrum", save=None, figsize=(10, 8), nbands=None, delta=0.01, quick=True, **kwargs): """Plots the phonon bands for the specified CLI args. Args: dbs (list): list of :class:`matdb.database.hessian.Hessian` `phonopy` calculation database instances that have DFT-accurate band information. fits (list): list of :class:`~matdb.fitting.basic.Trainer` to calculate bands for. dim (list): list of `int`; supercell dimensions for the phonon calculations. npts (int): number of points to sample along the special path in k-space. title (str): Override the default title for plotting; use `{}` for formatting chemical formula. save (str): name of a file to save the plot to; otherwise the plot is shown in a window. figsize (tuple): tuple of `float`; the size of the figure in inches. delta (float): size of displacement for finite difference derivative. nbands (int): number of bands to plot. quick (bool): when True, use symmetry to speed up the Hessian calculation for the specified potentials. kwargs (dict): additional "dummy" arguments so that this method can be called with arguments to other functions. """ if len(dbs) == 0: raise ValueError("Must specify at least one Hessian group " "to plot phonons for.") db = dbs[0] if db.atoms is not None: ratoms = db.atoms else: #Use the parent group; this one must have been one of the sub-sequence #recursively generated groups. ratoms = db.parent.atoms title = title.format(ratoms.get_chemical_formula()) nlines = len(dbs) + (0 if fits is None else len(fits)) colors = plt.cm.nipy_spectral(np.linspace(0, 1, nlines)) bands, style = {}, {} names, kpath = parsed_kpath(ratoms) #matplotlib needs the $ signs for latex if we are using special #characters. We only get names out from the first configuration; all #the others have to use the same one. names = ["${}$".format(n) if '\\' in n or '_' in n else n for n in names] for dbi, db in enumerate(dbs): db.calc_bands() bands[db.key] = db.bands style[db.key] = {"color": colors[dbi], "lw": 2} #All of the phonon calculations use the same base atoms configuration. The #last `phondb` in the enumerated list is as good as any other. if fits is not None: for fiti, fit in enumerate(tqdm(fits)): gi = len(dbs) + fiti ai = ratoms.copy() ai.set_calculator(fit.calculator) H = phon_calc(ai, supercell=db.supercell, delta=delta, quick=quick) bands[fit.fqn] = _calc_bands(db.atoms, H, db.supercell) style[fit.fqn] = {"color": colors[gi], "lw": 2} savefile = None if save: savefile = path.join(db.database.parent.plotdir, save) bandplot(bands, names, title=title, outfile=savefile, figsize=figsize, style=style, nbands=nbands)
def band_raw(primitive, bandfiles=None, pots=None, supercell=None, npts=100, title="{} Phonon Spectrum", save=None, figsize=(10, 8), nbands=4, line_names=None, delta=0.01, quick=True, **kwargs): """Plots the phonon bands from raw `band.yaml` files. Args: primitive (str): path to the atoms file for the *primitive* to plot bands for. Use the ASE format string as a prefix, e.g. `vasp-xml:vasprun.xml` or `extxyz:atoms.xyz`. Default assumes `vasp:{}` if no format is specified. bandfiles (list): list of `str` file paths to the plain `band.yaml` files. supercell (list): list of `int`; supercell dimensions for the phonon calculations. npts (int): number of points to sample along the special path in k-space. title (str): Override the default title for plotting; use `{}` for formatting chemical formula. save (str): name of a file to save the plot to; otherwise the plot is shown in a window. figsize (tuple): tuple of `float`; the size of the figure in inches. nbands (int): number of bands to plot. delta (float): size of displacement for finite difference derivative. quick (bool): when True, use symmetry to speed up the Hessian calculation for the specified potentials. kwargs (dict): additional "dummy" arguments so that this method can be called with arguments to other functions. """ nlines = len(bandfiles) + (0 if pots is None else len(pots)) colors = plt.cm.nipy_spectral(np.linspace(0, 1, nlines)) bands, style = {}, {} #Handle DSL format import on the file path for the primitive cell. if ':' not in primitive: atoms = Atoms(primitive, format="vasp") else: fmt, atpath = primitive.split(':') atoms = Atoms(atpath, format=fmt) names, kpath = parsed_kpath(atoms) #matplotlib needs the $ signs for latex if we are using special #characters. We only get names out from the first configuration; all #the others have to use the same one. names = ["${}$".format(n) if '\\' in n or '_' in n else n for n in names] for ifile, fpath in enumerate(bandfiles): if line_names is not None: key = line_names[ifile] else: key = "File {}".format(ifile) bands[key] = from_yaml(fpath) style[key] = {"color": colors[ifile], "lw": 2} if pots is not None: for fiti, fit in enumerate(tqdm(pots)): gi = len(bandfiles) + fiti atoms.set_calculator(fit) H = phon_calc(atoms, supercell=supercell, delta=delta, quick=quick) bands[line_names[gi]] = _calc_bands(atoms, H, supercell) style[line_names[gi]] = {"color": colors[gi], "lw": 2} title = title.format(atoms.get_chemical_formula()) savefile = None if save: savefile = save bandplot(bands, names, title=title, outfile=savefile, figsize=figsize, style=style, nbands=nbands)
def _calc_bands(atoms, hessian, supercell=(1, 1, 1), outfile=None, grid=None): """Calculates the band structure for the given Hessian matrix. Args: atoms (matdb.atoms.Atoms): atoms object corresponding to the *primitive* cell. The specified supercell matrix should result in a number of atoms that matches the dimensionality of the Hessian. supercell (tuple): tuple of `int` supercell matrix components; can have either 3 or 9 components. hessian (numpy.ndarray): with shape `(natoms*3, natoms*3)`. grid (list): list of `int` specifying the number of divisions in k-space along each reciprocal unit vector. outfile (str): path to the output `band.yaml` file that should be created by this function. Returns: If `outfile` is None, then this method returns a dictionary that has the same format as :func:`from_yaml`. """ #Create a temporary directory in which to work. target = mkdtemp() bandfile = path.join(target, "band.yaml") if grid is None: grid = [13, 13, 13] if isinstance(supercell, np.ndarray): supercell = supercell.flatten() #First, roll up the Hessian and write it as a FORCE_CONSTANTS file. with chdir(target): HR = roll(hessian) write_FORCE_CONSTANTS(HR) atoms.write("POSCAR", format="vasp") #We need to create the band.conf file and write the special #paths in k-space at which the phonons should be calculated. atom_types = _ordered_unique(atoms.get_chemical_symbols()) settings = [("FORCE_CONSTANTS", "READ"), ("ATOM_NAME", ' '.join(atom_types)), ("DIM", ' '.join(map(str, supercell))), ("MP", ' '.join(map(str, grid)))] labels, bands = parsed_kpath(atoms) bandfmt = "{0:.3f} {1:.3f} {2:.3f}" sband = [] for Q in bands: sband.append(bandfmt.format(*Q)) settings.append(("BAND", " ".join(sband))) settings.append(("BAND_LABELS", ' '.join(labels))) with open("band.conf", 'w') as f: for k, v in settings: f.write("{} = {}\n".format(k, v)) sargs = ["phonopy", "band.conf"] xres = execute(sargs, target, venv=True) if not path.isfile(bandfile): #pragma: no cover msg.err("could not calculate phonon bands; see errors.") msg.std(''.join(xres["output"])) result = None if outfile is not None: #Move the band.yaml file to the new target location. from shutil import move move(bandfile, outfile) else: result = from_yaml(bandfile) #Remove the temporary directory that we created and return the result. rmtree(target) return result