Source code for ixdat.plotters.ec_plotter

import numpy as np
from .base_mpl_plotter import MPLPlotter
from .plotting_tools import color_axis


[docs]class ECPlotter(MPLPlotter): """A matplotlib plotter specialized in electrochemistry measurements.""" def __init__(self, measurement=None): """Initiate the ECPlotter with its default Meausurement to plot""" self.measurement = measurement
[docs] def plot_measurement( self, *, measurement=None, tspan=None, V_str=None, J_str=None, axes=None, V_color="k", J_color="r", **kwargs, ): """Plot two variables on two y-axes vs time All arguments are optional. By default it plots potential in black on the left y-axis and current in red on the right y-axis, using data from its entire measurement. The axes are colored to match the traces and labled witht the respective series names. Args: measurement (Measurement): The measurement to plot, if not the one the plotter was initiated with. tspan (iter of float): The timespan (wrt to measurement.tstamp) to plot. V_str (string): The name of the ValueSeries to plot on the left y-axis. Defaults to measurement.V_str, which for an ECMeasurement is the name of its most calibrated/correct potential. J_str (string): The name of the ValueSeries to plot on the right y-axis. Defaults to measurement.J_str, which for an ECMeasurement is the name of its most normalized/correct current. axes (list of matplotlib.Axis): Two axes to plot on, if not the default new twinx()'d axes. axes[0] is for V_str and axes[1] for J_str. V_color (str): The color to plot the series called V_str. Defaults to black. J_color (str): The color to plot the series called J_str. Defaults to red. kwargs (dict): Additional key-word arguments are passed to matplotlib's plot() function, for both potential and current. Returns list of matplotlib.pyplot.Axis: The axes plotted on. """ measurement = measurement or self.measurement V_str = V_str or measurement.potential.name J_str = J_str or measurement.current.name t_v, v = measurement.grab(V_str, tspan=tspan, include_endpoints=False) t_j, j = measurement.grab(J_str, tspan=tspan, include_endpoints=False) if axes: ax1, ax2 = axes else: ax1 = self.new_ax() ax2 = ax1.twinx() axes = [ax1, ax2] ax1.plot(t_v, v, "-", color=V_color, label=V_str, **kwargs) ax2.plot(t_j, j, "-", color=J_color, label=J_str, **kwargs) ax1.set_xlabel("time / [s]") ax1.set_ylabel(V_str) ax2.set_ylabel(J_str) color_axis(ax1, V_color, lr="left") color_axis(ax2, J_color, lr="right") return axes
[docs] def plot_vs_potential( self, measurement=None, tspan=None, V_str=None, J_str=None, ax=None, **kwargs ): """Plot an ECMeasurement with electrode potential on the x-axis. This can actually plot with anything on the x-axis, by specifying what you want on the x-axis using V_str. The y-axis variable, which can be specified by J_str, is interpolated onto the time corresponding to the x-axis variable. TODO: This is a special case of the not-yet-implemented generalized TODO: `plot_vs`. Consider an inheritance structure to reduce redundancy in TODO: future plotters. All arguments are optional. By default it will plot current vs potential in black on a single axis for the whole experiment. TODO: color gradient (cmap=inferno) from first to last cycle. Args: measurement (Measurement): What to plot. Defaults to the measurement the plotter was initialized with tspan (iter of float): The timespan, relative to vs measurement.tstamp, on which to plot. V_str (str): Name of the x-axis ValueSeries. Defaults to calibrated potential J_str (str): Name of the y-axis ValueSeries. Defaults to normalized current. ax (matplotlib.pyplot.Axis): The axis to plot on, if not a new one. kwargs: Additional key-word arguments are passed to matplotlib's plot() function, including `color`. Returns matplotlib.pyplot.axis: The axis plotted on. """ measurement = measurement or self.measurement V_str = V_str or measurement.potential.name J_str = J_str or measurement.current.name t_v, v = measurement.grab(V_str, tspan=tspan, include_endpoints=False) t_j, j = measurement.grab(J_str, tspan=tspan, include_endpoints=False) j_v = np.interp(t_v, t_j, j) if not ax: ax = self.new_ax() if "color" not in kwargs: kwargs["color"] = "k" ax.plot(v, j_v, **kwargs) ax.set_xlabel(V_str) ax.set_ylabel(J_str) return ax
[docs]class CVDiffPlotter(MPLPlotter): """A matplotlib plotter for highlighting the difference between two cv's.""" def __init__(self, measurement=None): """Initiate the ECPlotter with its default CyclicVoltammagramDiff to plot""" self.measurement = measurement def plot(self, measurement=None, ax=None): measurement = measurement or self.measurement ax = ECPlotter.plot_vs_potential( self, measurement=measurement.cv_1, axes=ax, color="g" ) ax = ECPlotter.plot_vs_potential( self, measurement=measurement.cv_2, ax=ax, color="k", linestyle="--" ) t1, v1 = measurement.cv_1.grab("potential") j1 = measurement.cv_1.grab_for_t("current", t=t1) j_diff = measurement.grab_for_t("current", t=t1) # a mask which is true when cv_1 had bigger current than cv_2: v_scan = measurement.scan_rate.data mask = np.logical_xor(0 < j_diff, v_scan < 0) ax.fill_between(v1, j1 - j_diff, j1, where=mask, alpha=0.2, color="g") ax.fill_between( v1, j1 - j_diff, j1, where=np.logical_not(mask), alpha=0.1, hatch="//", color="g", ) return ax def plot_measurement(self, measurement=None, axes=None, **kwargs): measurement = measurement or self.measurement return ECPlotter.plot_measurement( self, measurement=measurement, axes=axes, **kwargs ) def plot_diff(self, measurement=None, tspan=None, ax=None): measurement = measurement or self.measurement t, v = measurement.grab("potential", tspan=tspan, include_endpoints=False) j_diff = measurement.grab_for_t("current", t) v_scan = measurement.scan_rate.data # a mask which is true when cv_1 had bigger current than cv_2: mask = np.logical_xor(0 < j_diff, v_scan < 0) if not ax: ax = self.new_ax() ax.plot(v[mask], j_diff[mask], "k-", label="cv1 > cv2") ax.plot( v[np.logical_not(mask)], j_diff[np.logical_not(mask)], "k--", label="cv1 < cv2", ) return ax
[docs] def plot_vs_potential(self): """FIXME: This is needed to satisfy ECMeasurement.__init__""" pass