Exemplo n.º 1
0
 def test_absorption(self):
     energy, properties = calculate_dielectric_properties(
         self.ge_diel,
         {"absorption"},
     )
     self.assertIsNone(
         assert_almost_equal(properties["absorption"], self.ge_abs))
Exemplo n.º 2
0
 def test_absorption(self):
     energy, alpha = calculate_dielectric_properties(
         self.ge_diel,
         {
             "absorption",
         },
     )["absorption"]
     self.assertIsNone(assert_almost_equal(alpha, self.ge_abs))
Exemplo n.º 3
0
def optplot(
    modes=("absorption", ),
    filenames=None,
    codes="vasp",
    prefix=None,
    directory=None,
    gaussian=None,
    band_gaps=None,
    labels=None,
    average=True,
    height=6,
    width=6,
    xmin=0,
    xmax=None,
    ymin=0,
    ymax=1e5,
    colours=None,
    style=None,
    no_base_style=None,
    image_format="pdf",
    dpi=400,
    plt=None,
    fonts=None,
    units="eV",
):
    """A script to plot optical absorption spectra from VASP calculations.

    Args:
        modes (:obj:`list` or :obj:`tuple`):
            Ordered list of :obj:`str` determining properties to plot.
            Accepted options are 'absorption' (default), 'eps', 'eps-real',
            'eps-im', 'n', 'n-real', 'n-im', 'loss' (equivalent to n-im).
        filenames (:obj:`str` or :obj:`list`, optional): Path to data file.
            For VASP this is a *vasprun.xml* file (can be gzipped); for
            Questaal the *opt.ext* file from *lmf* or *eps_BSE.out* from
            *bethesalpeter* may be used.
            Alternatively, a list of paths can be
            provided, in which case the absorption spectra for each will be
            plotted concurrently.
        codes (:obj:`str` or :obj:`list`, optional): Original
            calculator. Accepted values are 'vasp' and 'questaal'. Items should
            correspond to filenames.
        prefix (:obj:`str`, optional): Prefix for file names.
        directory (:obj:`str`, optional): The directory in which to save files.
        gaussian (:obj:`float`): Standard deviation for gaussian broadening.
        band_gaps (:obj:`float`, :obj:`str` or :obj:`list`, optional): The band
            gap as a :obj:`float`, in eV, plotted as a dashed line. If plotting
            multiple spectra then a :obj:`list` of band gaps can be provided.
            Band gaps can be provided as a floating-point number or as a path
            to a *vasprun.xml* file. To skip over a line, set its bandgap to
            zero or a negative number to place it outside the visible range.
        labels (:obj:`str` or :obj:`list`): A label to identify the spectra.
            If plotting multiple spectra then a :obj:`list` of labels can
            be provided.
        average (:obj:`bool`, optional): Average the dielectric response across
            all lattice directions. Defaults to ``True``.
        height (:obj:`float`, optional): The height of the plot.
        width (:obj:`float`, optional): The width of the plot.
        xmin (:obj:`float`, optional): The minimum energy on the x-axis.
        xmax (:obj:`float`, optional): The maximum energy on the x-axis.
        ymin (:obj:`float`, optional): The minimum absorption intensity on the
            y-axis.
        ymax (:obj:`float`, optional): The maximum absorption intensity on the
            y-axis.
        colours (:obj:`list`, optional): A :obj:`list` of colours to use in the
            plot. The colours can be specified as a hex code, set of rgb
            values, or any other format supported by matplotlib.
        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`.
        units (:obj:`str`, optional): X-axis units for the plot. 'eV' for
           energy in electronvolts or 'nm' for wavelength in nanometers.
           Defaults to 'eV'.

    Returns:
        A matplotlib pyplot object.
    """

    # Don't write files if this is being done to manipulate existing plt
    save_files = False if plt else True

    # BUILD LIST OF FILES AUTOMATICALLY IF NECESSARY

    if codes == "vasp":
        if not filenames:
            if os.path.exists("vasprun.xml"):
                filenames = ["vasprun.xml"]
            elif os.path.exists("vasprun.xml.gz"):
                filenames = ["vasprun.xml.gz"]
            else:
                logging.error("ERROR: No vasprun.xml found!")
                sys.exit()

    elif codes == "questaal":
        if not filenames:
            if len(glob("opt.*")) > 0:
                filenames = glob("opt.*")
                if len(filenames) == 1:
                    logging.info("Found optics file: " + filenames[0])
                else:
                    logging.info("Found optics files: " + ", ".join(filenames))

    if isinstance(filenames, str):
        filenames = [filenames]

    if isinstance(codes, str):
        codes = [codes] * len(filenames)
    elif len(codes) == 1:
        codes = list(codes) * len(filenames)

    # ITERATE OVER FILES READING DIELECTRIC DATA

    dielectrics = []
    auto_labels = []
    auto_band_gaps = []
    for i, (filename, code) in enumerate(zip(filenames, codes)):
        if code == "vasp":
            vr = Vasprun(filename)
            dielectrics.append(vr.dielectric)

            auto_labels.append(
                latexify(
                    vr.final_structure.composition.reduced_formula).replace(
                        "$_", r"$_\mathregular"))

            if isinstance(band_gaps, list) and not band_gaps:
                # band_gaps = [], auto band gap requested
                auto_band_gaps.append(
                    vr.get_band_structure().get_band_gap()["energy"])
            else:
                auto_band_gaps.append(None)

        elif code == "questaal":
            if not save_files:
                out_filename = None
            elif len(filenames) == 1:
                out_filename = "dielectric.dat"
            else:
                out_filename = f"dielectric_{i + 1}.dat"

            dielectrics.append(
                questaal.dielectric_from_file(filename, out_filename))

            auto_band_gaps.append(None)
            auto_labels.append(filename.split(".")[-1])
            if isinstance(band_gaps, list) and not band_gaps:
                logging.info("Bandgap requested but not supported for Questaal"
                             " file {}: skipping...".format(filename))

        else:
            raise Exception(f'Code selection "{code}" not recognised')

    if not labels and len(filenames) > 1:
        labels = auto_labels

    # PROCESS DIELECTRIC DATA: BROADENING AND DERIVED PROPERTIES

    if gaussian:
        dielectrics = [broaden_eps(d, gaussian) for d in dielectrics]

    # initialize spectrum data ready to append from each dataset
    abs_data = OrderedDict()

    for mode in modes:
        abs_data.update({mode: []})

    # for each calculation, get all required properties and append to data
    for d in dielectrics:
        # TODO: add support for other eigs and full modes
        energies, properties = calculate_dielectric_properties(
            d, set(modes), mode="average" if average else "trace")
        for mode, spectrum in properties.items():
            abs_data[mode].append((energies, spectrum))

    if isinstance(band_gaps, list) and not band_gaps:
        # empty list therefore use bandgaps collected from vasprun files
        band_gaps = auto_band_gaps
    elif isinstance(band_gaps, list):
        # list containing filenames and/or values: mutate the list in-place
        for i, item in enumerate(band_gaps):
            if item is None:
                pass
            elif _floatable(item):
                band_gaps[i] = float(item)
            elif "vasprun" in item:
                band_gaps[i] = (Vasprun(
                    item).get_band_structure().get_band_gap()["energy"])
            else:
                raise ValueError(
                    f"Format not recognised for auto bandgap: {item}.")

    plotter = SOpticsPlotter(abs_data, band_gap=band_gaps, label=labels)
    plt = plotter.get_plot(
        width=width,
        height=height,
        xmin=xmin,
        xmax=xmax,
        ymin=ymin,
        ymax=ymax,
        colours=colours,
        dpi=dpi,
        plt=plt,
        fonts=fonts,
        style=style,
        no_base_style=no_base_style,
        units=units,
    )

    if save_files:
        basename = "absorption"
        if prefix:
            basename = f"{prefix}_{basename}"
        image_filename = f"{basename}.{image_format}"

        if directory:
            image_filename = os.path.join(directory, image_filename)
        plt.savefig(image_filename, format=image_format, dpi=dpi)
        for mode, data in abs_data.items():
            basename = "absorption" if mode == "abs" else mode
            write_files(data,
                        basename=basename,
                        prefix=prefix,
                        directory=directory)
    else:
        return plt
Exemplo n.º 4
0
def optplot(modes=('absorption', ),
            filenames=None,
            prefix=None,
            directory=None,
            gaussian=None,
            band_gaps=None,
            labels=None,
            average=True,
            height=6,
            width=6,
            xmin=0,
            xmax=None,
            ymin=0,
            ymax=1e5,
            colours=None,
            style=None,
            no_base_style=None,
            image_format='pdf',
            dpi=400,
            plt=None,
            fonts=None):
    """A script to plot optical absorption spectra from VASP calculations.

    Args:
        modes (:obj:`list` or :obj:`tuple`):
            Ordered list of :obj:`str` determining properties to plot.
            Accepted options are 'absorption' (default), 'eps', 'eps-real',
                'eps-im', 'n', 'n-real', 'n-im', 'loss' (equivalent to n-im).
        filenames (:obj:`str` or :obj:`list`, optional): Path to vasprun.xml
            file (can be gzipped). Alternatively, a list of paths can be
            provided, in which case the absorption spectra for each will be
            plotted concurrently.
        prefix (:obj:`str`, optional): Prefix for file names.
        directory (:obj:`str`, optional): The directory in which to save files.
        gaussian (:obj:`float`): Standard deviation for gaussian broadening.
        band_gaps (:obj:`float` or :obj:`list`, optional): The band gap as a
            :obj:`float`, plotted as a dashed line. If plotting multiple
            spectra then a :obj:`list` of band gaps can be provided.
        labels (:obj:`str` or :obj:`list`): A label to identify the spectra.
            If plotting multiple spectra then a :obj:`list` of labels can
            be provided.
        average (:obj:`bool`, optional): Average the dielectric response across
            all lattice directions. Defaults to ``True``.
        height (:obj:`float`, optional): The height of the plot.
        width (:obj:`float`, optional): The width of the plot.
        xmin (:obj:`float`, optional): The minimum energy on the x-axis.
        xmax (:obj:`float`, optional): The maximum energy on the x-axis.
        ymin (:obj:`float`, optional): The minimum absorption intensity on the
            y-axis.
        ymax (:obj:`float`, optional): The maximum absorption intensity on the
            y-axis.
        colours (:obj:`list`, optional): A :obj:`list` of colours to use in the
            plot. The colours can be specified as a hex code, set of rgb
            values, or any other format supported by matplotlib.
        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 not filenames:
        if os.path.exists('vasprun.xml'):
            filenames = ['vasprun.xml']
        elif os.path.exists('vasprun.xml.gz'):
            filenames = ['vasprun.xml.gz']
        else:
            logging.error('ERROR: No vasprun.xml found!')
            sys.exit()

    elif isinstance(filenames, str):
        filenames = [filenames]

    vrs = [Vasprun(f) for f in filenames]
    dielectrics = [vr.dielectric for vr in vrs]

    if gaussian:
        dielectrics = [broaden_eps(d, gaussian) for d in dielectrics]

    # initialize spectrum data ready to append from each dataset
    abs_data = OrderedDict()

    for mode in modes:
        abs_data.update({mode: []})

    # for each calculation, get all required properties and append to data
    for d in dielectrics:
        for mode, spectrum in calculate_dielectric_properties(
                d, set(modes), average=average).items():
            abs_data[mode].append(spectrum)

    if isinstance(band_gaps, list) and not band_gaps:
        # empty list therefore get bandgap from vasprun files
        band_gaps = [
            vr.get_band_structure().get_band_gap()['energy'] for vr in vrs
        ]
    elif isinstance(band_gaps, list) and 'vasprun' in band_gaps[0]:
        # band_gaps contains list of vasprun files
        bg_vrs = [Vasprun(f) for f in band_gaps]
        band_gaps = [
            vr.get_band_structure().get_band_gap()['energy'] for vr in bg_vrs
        ]
    elif isinstance(band_gaps, list):
        # band_gaps is non empty list w. no vaspruns; presume floats
        band_gaps = [float(i) for i in band_gaps]

    save_files = False if plt else True

    if len(abs_data) > 1 and not labels:
        labels = [
            latexify(vr.final_structure.composition.reduced_formula).replace(
                '$_', '$_\mathregular') for vr in vrs
        ]

    plotter = SOpticsPlotter(abs_data, band_gap=band_gaps, label=labels)
    plt = plotter.get_plot(width=width,
                           height=height,
                           xmin=xmin,
                           xmax=xmax,
                           ymin=ymin,
                           ymax=ymax,
                           colours=colours,
                           dpi=dpi,
                           plt=plt,
                           fonts=fonts,
                           style=style,
                           no_base_style=no_base_style)

    if save_files:
        basename = 'absorption'
        if prefix:
            basename = '{}_{}'.format(prefix, basename)
        image_filename = '{}.{}'.format(basename, image_format)

        if directory:
            image_filename = os.path.join(directory, image_filename)
        plt.savefig(image_filename, format=image_format, dpi=dpi)
        for mode, data in abs_data.items():
            basename = 'absorption' if mode == 'abs' else mode
            write_files(data,
                        basename=basename,
                        prefix=prefix,
                        directory=directory)
    else:
        return plt