Source code for tp.plot.mfp

"""Function for plotting mean free path on the x-axis."""

#Functions
#---------
#
#    add_cum_kappa:
#        cumulative kappa vs mean free path.
#
#    add_markers:
#        add markers lines to a linear plot.
#"""

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import os
import tp.settings
import warnings
import yaml
from tp.data import utilities

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_cum_kappa(ax, data, kmin=1, temperature=300, direction='avg', label=None, xmarkers=None, ymarkers=None, add_xticks=False, add_yticks=False, main=True, scale=False, colour=None, fill=False, fillcolour=0.2, line=True, linestyle='-', marker=None, markerkwargs={}, verbose=False, **kwargs): """Cumulates and plots kappa against mean free path. Can plot data from multiple data dictionaries and directions. Colour, linestyle etc. are looped, so if you have two datasets and two directions, but only specify two colours, the first will apply to the first direction in both datasets, whereas if you want one for the first dataset and one for the second, you would repeat the first colour twice and the second twice too. Arguments --------- ax : axes axes to plot on. data : dict (list of sets of) Phono3py data including: mode_kappa: array-like frequency and q-point decomposed lattice thermal conductivity. mean_free_path : array-like mean free paths. temperature : array-like temperature. kmin : float or int, optional minimum kappa to plot in percent. Default: 1. temperature : float, optional temperature in K (finds nearest). Default: 300. direction : str, optional (list of) direction(s) from anisotropic data, accepts x-z/ a-c or average/ avg. Default: average. label : str, optional (list of) legend label(s). Defaults to $\mathregular{\kappa_l}$ if there is only one line plotted, or direction if there are more. xmarkers : array-like, optional mark kappa at certain mean free paths. ymarkers : array-like, optional mark mean free path at certain kappas. add_xticks : bool, optional add x_ticks for each marker. Doesn't work on log axes. Default: False. add_yticks : bool, optional add y_ticks for each marker. Doesn't work on log axes. Default: False. main : bool, optional set ticks, labels, limits. Default: True. scale : bool, optional if main, scale to percent. If not main, scale to axis limits. Default: False. colour : str or list, optional (list of) RGB line colour(s). Default: default colour cycle. fill : bool, optional fill below lines. Default: False. fillcolour : int or str or list, optional if a float from 0-1 and colour in #RRGGBB format, sets fill colour opacity. Otherwise treats it as a colour. Default: 0.2. line : bool, optional plot lines. Default: True. linestyle : str or list, optional (list of) linestyle(s). Default: "-". marker : str or list, optional (list of) markers. Default: None. verbose : bool, optional Write actual temperature used if applicable. Default: False. markerkwargs : dict, optional keyword arguments for the markers, passed to matplotlib.pyplot.plot. 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: color: black rasterized: False kwargs keyword arguments passed to matplotlib.pyplot.fill_between if filled or matplotlib.pyplot.plot otherwise. Defaults are defined below, which are overridden by those in ``~/.config/tprc.yaml``, both of which are overridden by arguments passed to this function. Default: rasterized: False Returns ------- None adds plot directly to ax. """ from tp.calculate import cumulate # defaults defkwargs = {'rasterized': False} if conf is None or 'mfp_cum_kappa_kwargs' not in conf or \ conf['mfp_cum_kappa_kwargs'] is None: kwargs = {**defkwargs, **kwargs} else: kwargs = {**defkwargs, **conf['mfp_cum_kappa_kwargs'], **kwargs} # input checks for name, value in zip(['main', 'scale', 'fill', 'line', 'add_xticks', 'add_yticks'], [ main, scale, fill, line, add_xticks, add_yticks]): assert isinstance(value, bool), '{} must be True or False.'.format(name) assert fill or line, 'fill or line or both must be True.' # data formatting and calculation if isinstance(direction, str): direction = [direction] if isinstance(data, dict): data = [data] if colour is None: colour = plt.rcParams['axes.prop_cycle'].by_key()['color'] elif isinstance(colour, str): colour = [colour] if fillcolour is None: fillcolour = plt.rcParams['axes.prop_cycle'].by_key()['color'] elif isinstance(fillcolour, (str, float, int)): fillcolour = [fillcolour] if isinstance(linestyle, str): linestyle = [linestyle] if marker is None or isinstance(marker, str): marker = [marker] if label is None: if len(data) == 1 and len(direction) == 1: label = [r'$\mathregular{\kappa_l}$'] else: label = direction elif isinstance(label, str): label = [label] mfpmin, mfpmax, kmax = None, None, None xticks, yticks = [], [] i = 0 for dat in data: for d in direction: data2 = utilities.resolve(dat, ['mode_kappa', 'mean_free_path'], temperature=temperature, direction=d) k = np.ravel(data2['mode_kappa']) mfp = np.abs(np.ravel(data2['mean_free_path'])) mfp, k = cumulate(mfp, k) mindex = next(x[0] for x in enumerate(np.ma.masked_invalid(k).compressed()) if x[1] > kmin*k[-1]/100) if mfpmin is None or mfpmin > mfp[mindex]: mfpmin = mfp[mindex] if mfpmax is None or mfpmax < mfp[-1]: mfpmax = np.nanmax(mfp) if kmax is None or kmax < k[-1]: kmax = 100 if main and scale else np.nanmax(k[-1]) mfp = np.append(mfp, 100*mfp[-1]) k = np.append(k, k[-1]) # percentage output if scale: axscale = [0, 100] if main else None k = tp.plot.utilities.scale_to_axis(ax, k, scale=axscale) colour1 = colour[i % len(colour)] fillcolour1 = fillcolour[i % len(fillcolour)] linestyle1 = linestyle[i % len(linestyle)] marker1 = marker[i % len(marker)] if label is not None: label1 = label[i % len(label)] else: label1 = None # colour # Tries to read the colour as an rgb code, then alpha value. if fill: try: fillcolour2 = mpl.colors.to_rgba(colour1, fillcolour1) except ValueError: if isinstance(colour1, list) and \ isinstance(fillcolour1, (float, int)) and \ fillcolour1 >= 0 and fillcolour1 <= 1: fillcolour2 = colour1 if len(colour1) == 3: fillcolour2.append(fillcolour1) elif len(colour1) == 4: fillcolour2[3] = fillcolour1 else: fillcolour2 = fillcolour1 # plotting if fill and line: ax.plot(mfp, k, color=colour1, linestyle=linestyle1, marker=marker1, label=label1, **kwargs) ax.fill_between(mfp, k, facecolor=fillcolour2, edgecolor=colour) elif fill and not line: ax.fill_between(mfp, k, facecolor=fillcolour2, **kwargs) else: ax.plot(mfp, k, color=colour1, linestyle=linestyle1, marker=marker1, label=label1, **kwargs) if xmarkers is not None or ymarkers is not None: xtick, ytick = add_markers(ax, mfp, k, xmarkers, ymarkers, **markerkwargs) xticks.append(xtick) yticks.append(ytick) i += 1 if verbose: print('Using {} {}.'.format(data2['meta']['temperature'], data2['meta']['units']['temperature'])) # axes formatting if main: axlabels = tp.settings.labels() if scale: ax.set_ylabel(axlabels['cumulative_percent']) else: ax.set_ylabel(axlabels['cumulative_kappa']) ax.set_xlabel(axlabels['mean_free_path']) ax.set_ylim(0, kmax) ax.set_xlim(mfpmin, mfpmax) tp.plot.utilities.set_locators(ax, x='log', y='linear') if add_xticks: ax.set_xticks(np.append(ax.get_xticks(), xticks)) if add_yticks: ax.set_yticks(np.append(ax.get_yticks(), yticks)) return
[docs]def add_markers(ax, x, y, xmarkers=None, ymarkers=None, **kwargs): """Adds marker lines linking a linear plot to the axes. Arguments --------- ax : axes axes to plot on. x : array-like x data. y : array-like y data. xmarkers : array-like, int or float, optional where on the x axis to mark. ymarkers : array-like, int or float, optional where on the y axis to mark. kwargs keyword arguments passed to matplotlib.pyplot.plot. 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: color: black linewidth: axes line width rasterized: False Returns ------- list added x locations. list added y locations. """ from scipy.interpolate import interp1d # defaults defkwargs = {'color': 'black', 'linewidth': ax.spines['bottom'].get_linewidth(), 'rasterized': False} if conf is None or 'marker_kwargs' not in conf or \ conf['marker_kwargs'] is None: kwargs = {**defkwargs, **kwargs} else: kwargs = {**defkwargs, **conf['marker_kwargs'], **kwargs} xticks, yticks = [], [] # plot x if xmarkers is not None: if isinstance(xmarkers, (int, float)): xmarkers = [xmarkers] yinter = interp1d(x, y) xmarky = yinter(xmarkers) for m in range(len(xmarkers)): ax.plot([xmarkers[m], xmarkers[m], 0], [0, xmarky[m], xmarky[m]], **kwargs) xticks.append(xmarkers) yticks.append(xmarky) # plot y if ymarkers is not None: if isinstance(ymarkers, (int, float)): ymarkers = [ymarkers] xinter = interp1d(y, x) ymarkx = xinter(ymarkers) for m in range(len(ymarkers)): ax.plot([ymarkx[m], ymarkx[m], 0], [0, ymarkers[m], ymarkers[m]], **kwargs) yticks.append(ymarkers) xticks.append(ymarkx) return xticks, yticks