Source code for tp.plot.heatmap

"""Heatmap plotters.

``add_heatmap`` is a base function, which handles axes limits, colourbar
formatting and extra colourmap options compared to ``matplotlib.pyplot.pcolormesh``.
The other functions plot specific quantites using this function.
"""

#Functions
#---------
#
#    add_heatmap:
#        heatmap.
#    add_pfmap:
#        power factor vs temperature and carrier concentration.
#    add_pfdiff:
#        power factor difference vs temperature and carrier concentration.
#    add_ztmap:
#        ZT vs temperature and carrier concentration.
#    add_ztdiff:
#        ZT difference vs temperature and carrier concentration.
#    add_kappa_target:
#        kappa_l needed to reach a given ZT.
#"""

from copy import copy
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import os
import tp
import warnings
import yaml
from scipy.interpolate import RectBivariateSpline as rbs

warnings.filterwarnings('ignore', module='matplotlib')

try:
    filename = '{}/.config/tprc.yaml'.format(os.path.expanduser("~"))
    with open(filename, 'r') as f:
        conf = yaml.safe_load(f)
except yaml.parser.ParserError:
    warnings.warn('Failed to read ~/.config/tprc.yaml')
    conf = None
except FileNotFoundError:
    conf = None

[docs]def add_heatmap(ax, x, y, c, xinterp=None, yinterp=None, order=1, xscale='linear', yscale='linear', cscale='linear', xmin=None, xmax=None, ymin=None, ymax=None, cmin=None, cmax=None, colour='viridis', undercolour=None, overcolour=None, discrete=False, levels=None, contours=None, contourcolours='black', contourkwargs=None, **kwargs): """Adds a heatmap to a set of axes. Formats limits, parses extra colourmap options, makes sure data isn't obscured. Arguments --------- ax : axes axes to plot on. x : array-like x data. y : array-like y data. c : array-like (shape: x, y) colour data. xinterp : int, optional density of interpolation. None turns it off. Default: None. yinterp : int, optional density of interpolation. None turns it off. Default: None. order : int, optional interpolation order (1=linear, 3=cubic etc.). Default: 1. xscale : str, optional x scale (linear or log). Default: linear. yscale : str, optional y scale (linear or log). Default: linear cscale : str, optional colour scale (linear or log). Default: linear. xmin : float, optional override x minimum. Default: None. xmax : float, optional override x maximum. Default: None. ymin : float, optional override y minimum. Default: None. ymax : float, optional override y maximum. Default: None. cmin : float, optional override colour scale minimum. Default: None. cmax : float, optional override colour scale maximum. Default: None. colour : colourmap or str or array-like or dict, optional colourmap or colourmap name or highlight colour or highlight, min, max colours in that order, or dictionary with mid and min and/or max keys. Colour format must be hex or rgb (array) or a named colour recognised by matplotlib. Default: Blues. discrete : bool, optional use colour bands rather than a continuously shifting colour sequence. Default: False. levels : int or array-like, optional sets levels for discrete plots. Lists directly specify the contour levels while integers specify the maximum-1 number of "nice" levels to plot. Default: None. undercolour : str or array-like, optional colour for values under cmin. Default: None. overcolour : str or array-like, optional colour for values over cmax. Default: None. contours : int or float or array-like, optional contours to plot. Default: None. contourcolours : string or array-like, optional colours of the contours. If fewer are supplied, they repeat. Default: black. contourkwargs : dict, optional keyword arguments passed to matplotlib.pyplot.contour. Default: None kwargs keyword arguments passed to matplotlib.pyplot.pcolormesh or matplotlib.pyplot.contourf. Defaults are defined below, which are overridden by those in ``~/.config/tprc.yaml``, both of which are overridden by arguments passed to this function. Defaults: rasterized: True Returns ------- colourbar colourbar. """ # defaults defkwargs = {'rasterized': True} if conf is None or 'heatmap_kwargs' not in conf or \ conf['heatmap_kwargs'] is None: kwargs = {**defkwargs, **kwargs} else: kwargs = {**defkwargs, **conf['heatmap_kwargs'], **kwargs} # data trimming x = np.array(x) y = np.array(y) c = np.array(c) if xmin is None: xmin = np.nanmin(x) if xmax is None: xmax = np.nanmax(x) if ymin is None: ymin = np.nanmin(y) if ymax is None: ymax = np.nanmax(y) xi = np.where((x >= xmin) & (x <= xmax))[0] yi = np.where((y >= ymin) & (y <= ymax))[0] y = y[yi] x = x[xi] try: c = c[np.ix_(xi, yi)] except IndexError: c = c[np.ix_(xi[:-1], yi[:-1])] # colour # Sets colourbar extension. extend = 'neither' if cmin is None: cmin = np.nanmin(c) elif cmin > np.nanmin(c): extend = 'min' if cmax is None: cmax = np.nanmax(c) elif cmax < np.nanmax(c): if extend == 'min': extend = 'both' else: extend = 'max' if cscale == 'linear': cnorm = mpl.colors.Normalize(vmin=cmin, vmax=cmax) elif cscale == 'log': cnorm = mpl.colors.LogNorm(vmin=cmin, vmax=cmax) # Tries to read as a colourmap or colourmap name, or uses a single # #rrggbb colour as the highlight colour for a tp.plot.colour.uniform. try: try: colours = copy(mpl.cm.get_cmap(colour)) except AttributeError: colours = copy(mpl.colormaps[colour]) except ValueError: if isinstance(colour, mpl.colors.ListedColormap): colours = colour elif isinstance(colour, str): colours = tp.plot.colour.uniform(colour) elif isinstance(colour, list): colours = tp.plot.colour.uniform(*colour) elif isinstance(colour, dict): colours = tp.plot.colour.uniform(**colour) else: raise Exception('colour must be a colourmap, colourmap ' 'name, single highlight colour or ' 'highlight, min, max colours in that ' 'order, or a dictionary with mid and min ' 'and/or max keys. Colour format must be ' 'hex or rgb (array) or a named colour ' 'recognised by matplotlib.') if undercolour is not None: colours.set_under(undercolour) if overcolour is not None: colours.set_over(overcolour) # data interpolation if xinterp is not None or yinterp is not None: rs = rbs(x, y, c, kx=order, ky=order) if xinterp is not None: if xscale == 'linear': x = np.linspace(x[0], x[-1], xinterp) if xscale == 'log': x = np.geomspace(x[0], x[-1], xinterp) if yinterp is not None: if yscale == 'linear': y = np.linspace(y[0], y[-1], yinterp) if yscale == 'log': y = np.geomspace(y[0], y[-1], yinterp) x = np.array(x) y = np.array(y) cinterp = lambda x, y: rs(x, y).T c = cinterp(x, y) else: c = np.transpose(c) # ensure all data is shown if len(x) == len(c[0]): x = list(x) if xscale == 'linear': x.append(2 * x[-1] - x[-2]) elif xscale =='log': x.append(x[-1] ** 2 / x[-2]) if len(y) == len(c): y = list(y) if yscale == 'linear': y.append(2 * y[-1] - y[-2]) elif yscale =='log': y.append(y[-1] ** 2 / y[-2]) # plotting if discrete: kwargs.pop('rasterized') if levels in [None, (), [None], (None,)]: levels = 10 else: if isinstance(levels, (list, np.ndarray, tuple)) and len(levels) == 1: levels = int(levels[0]) elif isinstance(levels, float): raise TypeError( 'levels must be an int or array-like, not float') heat = ax.contourf(x[:-1], y[:-1], c, cmap=colours, norm=cnorm, levels=levels, **kwargs) else: heat = ax.pcolormesh(x, y, c, cmap=colours, norm=cnorm, **kwargs) fig = ax.get_figure() if 'dos' in fig.__dict__ and fig.__dict__['dos']: # place colourbar outside dos cbar = plt.colorbar(heat, extend=extend) else: cbar = plt.colorbar(heat, ax=ax, extend=extend) # contours if contours not in [None, [None], (), (None,)]: if contourkwargs == None: contourkwargs = {} if not isinstance(contours, (list, np.ndarray)): contours = list(contours) if not isinstance(contourcolours, (list, np.ndarray)): contourcolours = list(contourcolours) if len(contours) > 1: ctnorm = [(ct-np.amin(contours))/ (np.amax(contours)-np.amin(contours)) for ct in contours] else: ctnorm = 0 cmap = None try: cmap = contourcolours try: contourcolours = mpl.cm.get_cmap(contourcolours)(ctnorm) except AttributeError: contourcolours = mpl.colormaps[contourcolours](ctnorm) plt.contour(x[:-1], y[:-1], c, contours, cmap=cmap, **contourkwargs) except ValueError: cmap = None if isinstance(contourcolours, mpl.colors.ListedColormap): cmap = contourcolours contourcolours = contourcolours(ctnorm) plt.contour(x[:-1], y[:-1], c, contours, cmap=cmap, **contourkwargs) elif not isinstance(contourcolours, (list, np.ndarray)): contourcolours = [contourcolours] while len(contourcolours) < len(contours): contourcolours += contourcolours if cmap is None: plt.contour(x[:-1], y[:-1], c, contours, colors=contourcolours, **contourkwargs) for i, c in enumerate(contours): cbar.ax.axhline(c, color=contourcolours[i], **contourkwargs) # axes formatting tp.plot.utilities.set_locators(ax, x=xscale, y=yscale) tp.plot.utilities.set_locators(cbar.ax, y=cscale) ax.set_xlim(x[0], x[-1]) ax.set_ylim(y[0], y[-1]) return cbar
[docs]def add_pfmap(ax, data, direction='avg', xinterp=200, yinterp=200, order=1, xmin=None, xmax=None, ymin=None, ymax=None, cmin=None, cmax=None, colour='viridis', discrete=False, levels=None, contours=None, contourcolours='black', contourkwargs=None, **kwargs): """Convenience wrapper for plotting power factor heatmaps. Calculates power factor, plots and formats labels etc. Arguments --------- ax : axes axes to plot on. data : dict dictionary containing temperature and doping, and either power_factor or conductivity and seebeck. direction : str, optional crystal direction, accepts x-z/ a-c or average/ avg. Default: average. xinterp : int, optional density of interpolation. None turns it off. Default: 200. yinterp : int, optional density of interpolation. None turns it off. Default: 200. order : int, optional interpolation order (1=linear, 3=cubic etc.). Default: 1. xmin : float, optional override temperature minimum. Default: None. xmax : float, optional override temperature maximum. Default: None. ymin : float, optional override doping minimum. Default: None. ymax : float, optional override doping maximum. Default: None. cmin : float, optional override ZT minimum. Default: None. cmax : float, optional override ZT maximum. Default: None. colour : colourmap or str or array-like, optional colourmap or colourmap name; or key colour or min and max RGB colours to generate a colour map. Colour format must be hex or rgb (array) or a named colour recognised by matplotlib. Default: viridis. discrete : bool, optional use colour bands rather than a continuously shifting colour sequence. Default: False. levels : int or array-like, optional sets levels for discrete plots. Lists directly specify the contour levels while integers specify the maximum-1 number of "nice" levels to plot. Default: None. contours : int or float or array-like, optional PF contours to plot. Default: None. contourcolours : string or array-like, optional colours of the PF contours. If fewer are supplied, they repeat. Default: black. contourkwargs : dict, optional keyword arguments passed to matplotlib.pyplot.contour. Default: None kwargs keyword arguments passed to matplotlib.pyplot.pcolormesh or matplotlib.pyplot.contourf. Defaults are defined below, which are overridden by those in ``~/.config/tprc.yaml``, both of which are overridden by arguments passed to this function. Defaults: rasterized: True Returns ------- colourbar colourbar. """ # defaults defkwargs = {'rasterized': True} if conf is None or 'pfmap_kwargs' not in conf or \ conf['pfmap_kwargs'] is None: kwargs = {**defkwargs, **kwargs} else: kwargs = {**defkwargs, **conf['pfmap_kwargs'], **kwargs} equants = ['conductivity', 'seebeck'] # data formatting if 'power_factor' in data: data = tp.data.utilities.resolve(data, 'power_factor', direction=direction) else: data = tp.data.utilities.resolve(data, equants, direction=direction) data = tp.calculate.power_factor_fromdict(data, use_tprc=True) # plotting cbar = add_heatmap(ax, data['temperature'], list(np.abs(data['doping'])), data['power_factor'], xinterp=xinterp, yinterp=yinterp, order=order, yscale='log', xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax, cmin=cmin, cmax=cmax, colour=colour, discrete=discrete, levels=levels, contours=contours, countourcolours=contourcolours, contourkwargs=contourkwargs, **kwargs) # axes formatting labels = tp.settings.labels() ax.set_xlabel(labels['temperature']) ax.set_ylabel(labels['doping']) cbar.set_label(labels['power_factor']) return cbar
[docs]def add_pfdiff(ax, data1, data2, direction='avg', xinterp=200, yinterp=200, order=1, xmin=None, xmax=None, ymin=None, ymax=None, cmin=None, cmax=None, colour1='#800080', colour2='#FF8000', midcolour='#FFFFFF', label1=None, label2=None, discrete=False, levels=None, contours=None, contourcolours='black', contourkwargs=None, **kwargs): """Plots a difference of two power factors heatmap. Calculates power factor, plots and formats labels etc. Arguments --------- ax : axes axes to plot on. data(1,2) : dict dictionaries containing temperature and doping, and either power_factor or conductivity and seebeck. The result is data1 - data2. direction : str, optional crystal direction, accepts x-z/ a-c or average/ avg. Default: average. xinterp : int, optional density of interpolation. None turns it off. Default: 200. yinterp : int, optional density of interpolation. None turns it off. Default: 200. order : int, optional interpolation order (1=linear, 3=cubic etc.). Default: 1. xmin : float, optional override temperature minimum. Default: None. xmax : float, optional override temperature maximum. Default: None. ymin : float, optional override doping minimum. Default: None. ymax : float, optional override doping maximum. Default: None. cmin : float, optional override ZT minimum. Default: None. cmax : float, optional override ZT maximum. Default: None. colour(1,2) : str, optional colours for data(1,2). Colour format must be hex or rgb (array) or a named colour recognised by matplotlib. Deault: #800080, #FF8000. midcolour : str, optional colour at 0 difference. Default: #FFFFFF. label(1,2) : str, optional labels for data(1,2) to go in a legend. Default: None. discrete : bool, optional use colour bands rather than a continuously shifting colour sequence. Default: False. levels : int or array-like, optional sets levels for discrete plots. Lists directly specify the contour levels while integers specify the maximum-1 number of "nice" levels to plot. Default: None. contours : int or float or array-like, optional PF contours to plot. Default: None. contourcolours : string or array-like, optional colours of the PF contours. If fewer are supplied, they repeat. Default: black. contourkwargs : dict, optional keyword arguments passed to matplotlib.pyplot.contour. Default: None kwargs keyword arguments passed to matplotlib.pyplot.pcolormesh or matplotlib.pyplot.contourf. Defaults are defined below, which are overridden by those in ``~/.config/tprc.yaml``, both of which are overridden by arguments passed to this function. Defaults: rasterized: True Returns ------- colourbar colourbar. list legend handles. list legend labels. """ # defaults defkwargs = {'rasterized': True} if conf is None or 'pfdiff_kwargs' not in conf or \ conf['pfdiff_kwargs'] is None: kwargs = {**defkwargs, **kwargs} else: kwargs = {**defkwargs, **conf['pfdiff_kwargs'], **kwargs} equants = ['conductivity', 'seebeck'] # data formatting data = [data1, data2] for i in [0, 1]: data[i]['doping'] = np.abs(data[i]['doping']) if 'power_factor' in data[i]: data[i] = tp.data.utilities.resolve(data[i], 'power_factor', direction=direction) else: data[i] = tp.data.utilities.resolve(data[i], equants, direction=direction) data[i] = tp.calculate.power_factor_fromdict(data[i], use_tprc=True) data[0], data[1] = tp.calculate.interpolate(*data, dependent='temperature', keys1='power_factor', keys2='power_factor') data[0], data[1] = tp.calculate.interpolate(*data, dependent='doping', keys1='power_factor', axis1=1, keys2='power_factor', axis2=1) diff = np.subtract(data[0]['power_factor'], data[1]['power_factor']) dmin, dmax = np.nanmin(diff), np.nanmax(diff) mid = dmin / (dmin - dmax) # colours # legend handles and labels h = [mpl.patches.Patch(facecolor=colour1, label=label1), mpl.patches.Patch(facecolor=colour2, label=label2)] l = [label1, label2] # if one is uniformly less than the other, its colour doesn't show if dmin > 0: colour2 = midcolour elif dmax < 0: colour1 = midcolour colour = tp.plot.colour.elbow(cmin=colour2, cmax=colour1, cmid=midcolour, midpoint=mid) # plotting cbar = add_heatmap(ax, data[0]['temperature'], list(data[0]['doping']), diff, xinterp=xinterp, yinterp=yinterp, order=order, yscale='log', xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax, cmin=cmin, cmax=cmax, colour=colour, discrete=discrete, levels=levels, contours=contours, contourcolours=contourcolours, contourkwargs=contourkwargs, **kwargs) # axes formatting labels = tp.settings.labels() ax.set_xlabel(labels['temperature']) ax.set_ylabel(labels['doping']) cbar.set_label('$\Delta$ '+labels['power_factor']) return cbar, h, l
[docs]def add_ztmap(ax, data, kdata=1., direction='avg', xinterp=200, yinterp=200, order=1, xmin=None, xmax=None, ymin=None, ymax=None, cmin=None, cmax=None, colour='viridis', discrete=False, levels=None, contours=None, contourcolours='black', contourkwargs=None, **kwargs): """Convenience wrapper for plotting ZT heatmaps. Calculates ZT, plots and formats labels etc. Arguments --------- ax : axes axes to plot on. data : dict dictionary containing temperature and doping, and either zt or conductivity, seebeck and electronic_thermal_conductivity. kdata : dict or int or float, optional dictionary containing lattice_thermal_conductivity and temperature. Ignored if zt in data. Can specify a constant value instead. Default: 1. direction : str, optional crystal direction, accepts x-z/ a-c or average/ avg. Default: average. xinterp : int, optional density of interpolation. None turns it off. Default: 200. yinterp : int, optional density of interpolation. None turns it off. Default: 200. order : int, optional interpolation order (1=linear, 3=cubic etc.). Default: 1. xmin : float, optional override temperature minimum. Default: None. xmax : float, optional override temperature maximum. Default: None. ymin : float, optional override doping minimum. Default: None. ymax : float, optional override doping maximum. Default: None. cmin : float, optional override ZT minimum. Default: None. cmax : float, optional override ZT maximum. Default: None. colour : colourmap or str or array-like, optional colourmap or colourmap name; or key colour or min and max colours to generate a colour map. Colour format must be hex or rgb (array) or a named colour recognised by matplotlib. Default: viridis. discrete : bool, optional use colour bands rather than a continuously shifting colour sequence. Default: False. levels : int or array-like, optional sets levels for discrete plots. Lists directly specify the contour levels while integers specify the maximum-1 number of "nice" levels to plot. Default: None. contours : int or float or array-like, optional ZT contours to plot. Default: None. contourcolours : string or array-like, optional colours of the ZT contours. If fewer are supplied, they repeat. Default: black. contourkwargs : dict, optional keyword arguments passed to matplotlib.pyplot.contour. Default: None kwargs keyword arguments passed to matplotlib.pyplot.pcolormesh or matplotlib.pyplot.contourf. Defaults are defined below, which are overridden by those in ``~/.config/tprc.yaml``, both of which are overridden by arguments passed to this function. Defaults: rasterized: True Returns ------- colourbar colourbar. """ # defaults defkwargs = {'rasterized': True} if conf is None or 'ztmap_kwargs' not in conf or \ conf['ztmap_kwargs'] is None: kwargs = {**defkwargs, **kwargs} else: kwargs = {**defkwargs, **conf['ztmap_kwargs'], **kwargs} ltc = 'lattice_thermal_conductivity' equants = ['conductivity', 'seebeck', 'electronic_thermal_conductivity'] # data formatting if 'zt' in data: data = tp.data.utilities.resolve(data, 'zt', direction=direction) else: if kdata is None: kdata = 1. if not isinstance(kdata, (int, float)): data, kdata = tp.calculate.interpolate(data, kdata, 'temperature', equants, ltc, kind='cubic') data[ltc] = kdata[ltc] data['meta']['dimensions'][ltc] = kdata['meta']['dimensions'][ltc] data['meta']['units'][ltc] = kdata['meta']['units'][ltc] data['meta']['kappa_source'] = kdata['meta']['kappa_source'] else: data[ltc] = kdata * np.ones(len(data['temperature'])) data['meta']['dimensions'][ltc] = ['temperature'] data['meta']['units'][ltc] = tp.settings.units()[ltc] data['meta']['kappa_source'] = 'Set to {} {}'.format(kdata, data['meta']['units'][ltc]) data = tp.data.utilities.resolve(data, [*equants, ltc], direction=direction) data = tp.calculate.zt_fromdict(data, use_tprc=True) # plotting cbar = add_heatmap(ax, data['temperature'], list(np.abs(data['doping'])), data['zt'], xinterp=xinterp, yinterp=yinterp, order=order, yscale='log', xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax, cmin=cmin, cmax=cmax, colour=colour, discrete=discrete, levels=levels, contours=contours, contourcolours=contourcolours, contourkwargs=contourkwargs, **kwargs) # axes formatting labels = tp.settings.labels() ax.set_xlabel(labels['temperature']) ax.set_ylabel(labels['doping']) cbar.set_label(labels['zt']) return cbar
[docs]def add_ztdiff(ax, data1, data2, kdata1=1., kdata2=1., direction='avg', xinterp=200, yinterp=200, order=1, xmin=None, xmax=None, ymin=None, ymax=None, cmin=None, cmax=None, colour1='#800080', colour2='#FF8000', midcolour='#FFFFFF', label1=None, label2=None, discrete=False, levels=None, contours=None, contourcolours='black', contourkwargs=None, **kwargs): """Plots a difference of two ZTs heatmap. Calculates ZT, plots and formats labels etc. Arguments --------- ax : axes axes to plot on. data(1,2) : dict dictionaries containing temperature and doping, and either zt or conductivity, seebeck and electronic_thermal_conductivity. The result is data1 - data2. kdata(1,2) : dict or int or float, optional dictionaries containing lattice_thermal_conductivity and temperature. Ignored if zt in data. Can specify constant values instead. Default: 1. direction : str, optional crystal direction, accepts x-z/ a-c or average/ avg. Default: average. xinterp : int, optional density of interpolation. None turns it off. Default: 200. yinterp : int, optional density of interpolation. None turns it off. Default: 200. order : int, optional interpolation order (1=linear, 3=cubic etc.). Default: 1. xmin : float, optional override temperature minimum. Default: None. xmax : float, optional override temperature maximum. Default: None. ymin : float, optional override doping minimum. Default: None. ymax : float, optional override doping maximum. Default: None. cmin : float, optional override ZT minimum. Default: None. cmax : float, optional override ZT maximum. Default: None. colour(1,2) : str, optional colours for data(1,2). Colour format must be hex or rgb (array) or a named colour recognised by matplotlib. Default: #800080, #FF8000. midcolour : str, optional colour at 0 difference. Default: #FFFFFF. label(1,2) : str, optional labels for data(1,2) to go in a legend. Default: None. discrete : bool, optional use colour bands rather than a continuously shifting colour sequence. Default: False. levels : int or array-like, optional sets levels for discrete plots. Lists directly specify the contour levels while integers specify the maximum-1 number of "nice" levels to plot. Default: None. contours : int or float or array-like, optional ZT contours to plot. Default: None. contourcolours : string or array-like, optional colours of the ZT contours. If fewer are supplied, they repeat. Default: black. contourkwargs : dict, optional keyword arguments passed to matplotlib.pyplot.contour. Default: None kwargs keyword arguments passed to matplotlib.pyplot.pcolormesh or matplotlib.pyplot.contourf. Defaults are defined below, which are overridden by those in ``~/.config/tprc.yaml``, both of which are overridden by arguments passed to this function. Defaults: rasterized: True Returns ------- colourbar colourbar. list legend handles. list legend labels. """ # defaults defkwargs = {'rasterized': True} if conf is None or 'ztdiff_kwargs' not in conf or \ conf['ztdiff_kwargs'] is None: kwargs = {**defkwargs, **kwargs} else: kwargs = {**defkwargs, **conf['ztdiff_kwargs'], **kwargs} ltc = 'lattice_thermal_conductivity' equants = ['conductivity', 'seebeck', 'electronic_thermal_conductivity'] # data formatting data = [data1, data2] kdata = [kdata1, kdata2] for i in [0, 1]: data[i]['doping'] = np.abs(data[i]['doping']) if 'zt' in data[i]: data[i] = tp.data.utilities.resolve(data[i], 'zt', direction=direction) else: if kdata[i] is None: kdata[i] = 1. if not isinstance(kdata[i], (int, float)): data[i], kdata[i] = tp.calculate.interpolate(data[i], kdata[i], 'temperature', equants, ltc, kind='cubic') data[i][ltc] = kdata[i][ltc] data[i]['meta']['dimensions'][ltc] = kdata[i]['meta']['dimensions'][ltc] data[i]['meta']['units'][ltc] = kdata[i]['meta']['units'][ltc] data[i]['meta']['kappa_source'] = kdata[i]['meta']['kappa_source'] else: data[i][ltc] = kdata[i] * np.ones(len(data[i]['temperature'])) data[i]['meta']['dimensions'][ltc] = ['temperature'] data[i]['meta']['units'][ltc] = tp.settings.units()[ltc] data[i]['meta']['kappa_source'] = 'Set to {} {}'.format(kdata[i], data[i]['meta']['units'][ltc]) data[i] = tp.data.utilities.resolve(data[i], [*equants, ltc], direction=direction) data[i] = tp.calculate.zt_fromdict(data[i], use_tprc=True) data[0], data[1] = tp.calculate.interpolate(*data, dependent='temperature', keys1='zt', keys2='zt') data[0], data[1] = tp.calculate.interpolate(*data, dependent='doping', keys1='zt', axis1=1, keys2='zt', axis2=1) diff = np.subtract(data[0]['zt'], data[1]['zt']) dmin, dmax = np.nanmin(diff), np.nanmax(diff) mid = dmin / (dmin - dmax) # colours # legend handles and labels h = [mpl.patches.Patch(facecolor=colour1, label=label1), mpl.patches.Patch(facecolor=colour2, label=label2)] l = [label1, label2] # if one is uniformly less than the other, its colour doesn't show if dmin > 0: colour2 = midcolour elif dmax < 0: colour1 = midcolour colour = tp.plot.colour.elbow(cmin=colour2, cmax=colour1, cmid=midcolour, midpoint=mid) # plotting cbar = add_heatmap(ax, data[0]['temperature'], list(data[0]['doping']), diff, xinterp=xinterp, yinterp=yinterp, order=order, yscale='log', xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax, cmin=cmin, cmax=cmax, colour=colour, discrete=discrete, levels=levels, contours=contours, contourcolours=contourcolours, contourkwargs=contourkwargs, **kwargs) # axes formatting labels = tp.settings.labels() ax.set_xlabel(labels['temperature']) ax.set_ylabel(labels['doping']) cbar.set_label('$\Delta$ '+labels['zt']) return cbar, h, l
[docs]def add_kappa_target(ax, data, zt=2, direction='avg', xinterp=200, yinterp=200, order=1, xmin=None, xmax=None, ymin=None, ymax=None, cmin=0, cmax=None, colour='viridis', negativecolour='grey', discrete=False, levels=None, contours=None, contourcolours='black', contourkwargs=None, **kwargs): """Plots a heatmap of k_latt required for a target ZT Calculates lattice thermal conductivity, plots and formats labels etc. May be useful to screen materials to calculate kappa_l for. Arguments --------- ax : axes axes to plot on. data : dict dictionary containing temperature and doping, and either zt or conductivity, seebeck and electronic_thermal_conductivity. zt : float, optional target ZT. Default: 2. direction : str, optional crystal direction, accepts x-z/ a-c or average/ avg. Default: average. xinterp : int, optional density of interpolation. None turns it off. Default: 200. yinterp : int, optional density of interpolation. None turns it off. Default: 200. order : int, optional interpolation order (1=linear, 3=cubic etc.). Default: 1. xmin : float, optional override temperature minimum. Default: None. xmax : float, optional override temperature maximum. Default: None. ymin : float, optional override doping minimum. Default: None. ymax : float, optional override doping maximum. Default: None. cmin : float, optional override kappa minimum. Default: 0. cmax : float, optional override kappa maximum. Default: None. colour : colourmap or str or array-like, optional colourmap or colourmap name; or key colour or min and max RGB colours to generate a colour map. Colour format must be hex or rgb (array) or a named colour recognised by matplotlib. Default: viridis. negativecolour : str or array-like, optional colour for values under cmin. Default: grey. discrete : bool, optional use colour bands rather than a continuously shifting colour sequence. Default: False. levels : int or array-like, optional sets levels for discrete plots. Lists directly specify the contour levels while integers specify the maximum-1 number of "nice" levels to plot. Default: None. contours : int or float or array-like, optional kappa contours to plot. Default: None. contourcolours : string or array-like, optional colours of the kappa contours. If fewer are supplied, they repeat. Default: black. contourkwargs : dict, optional keyword arguments passed to matplotlib.pyplot.contour. Default: None kwargs keyword arguments passed to matplotlib.pyplot.pcolormesh or matplotlib.pyplot.contourf. Defaults are defined below, which are overridden by those in ``~/.config/tprc.yaml``, both of which are overridden by arguments passed to this function. Defaults: rasterized: True Returns ------- colourbar colourbar. """ # defaults defkwargs = {'rasterized': True} if conf is None or 'kappa_target_kwargs' not in conf or \ conf['kappa_target_kwargs'] is None: kwargs = {**defkwargs, **kwargs} else: kwargs = {**defkwargs, **conf['kappa_target_kwargs'], **kwargs} ltc = 'lattice_thermal_conductivity' equants = ['conductivity', 'seebeck', 'electronic_thermal_conductivity'] # data formatting data['zt'] = zt data = tp.calculate.kl_fromdict(data, use_tprc=True) data = tp.data.utilities.resolve(data, ltc, direction=direction) # plotting cbar = add_heatmap(ax, data['temperature'], list(np.abs(data['doping'])), data[ltc], xinterp=xinterp, yinterp=yinterp, order=order, yscale='log', xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax, cmin=cmin, cmax=cmax, colour=colour, undercolour=negativecolour, discrete=discrete, levels=levels, contours=contours, contourcolours=contourcolours, contourkwargs=contourkwargs, **kwargs) # axes formatting labels = tp.settings.labels() ax.set_xlabel(labels['temperature']) ax.set_ylabel(labels['doping']) cbar.set_label(labels[ltc]) return cbar