ampworks.dqdv#

Functions for analyzing dQdV data from battery experiments. Provides curve extraction and smoothing, fitting methods, and post-processing of degradation modes from fitted stoichiometries.

Classes#

DegModeTable

Degradation modes table.

DqdvFitResult

Container for a single dQdV fit.

DqdvFitTable

Container for many dQdV fits.

DqdvFitter

Wrapper for dQdV fitting.

DqdvSpline

Smoothing spline for dQdV curves.

Functions#

calc_lam_lli(fit_table)

Calculate degradation modes.

plot_lam_lli(deg_table[, x_col, std, return_axs])

Plot degradation modes.

run_gui([jupyter_mode, jupyter_height])

Run a graphical dQdV fitting interface.

Package Contents#

class ampworks.dqdv.DegModeTable(df)[source]#

Degradation modes table.

Output container for calc_lam_lli. Stores capacities (Ah), loss of active material (LAM), loss of lithium inventory (LLI), and standard deviations (std). ‘n’, ‘p’, and ‘c’ in the column names refer to the negative electrode, positive electrode, and full cell, respectively. May also include extra columns inherited from the DqdvFitTable, if present.

Parameters:

df (pd.DataFrame) – The input dataframe to store.

See also

calc_lam_lli

Calculate degradation modes from fits.

copy()#

Returns a copy of the instance.

Returns:

table (Self) – A deep copy of the instance. Does not share memory with original.

classmethod from_csv(path)#

Create a new instance from a CSV file.

Parameters:

path (str or Path) – Path to the CSV file.

Returns:

table (Self) – A new instance initialized with data from the file.

to_csv(path)#

Write the table to a CSV file.

Parameters:

path (str or Path) – Path to the output file.

property df: pandas.DataFrame#

The underlying DataFrame stored in the container.

class ampworks.dqdv.DqdvFitResult(**kwargs)[source]#

Container for a single dQdV fit.

Container for fits from the DqdvFitter. An instance of this class is returned from both the grid search and constrained fit methods.

Attribute

Type

Description

success

bool

whether or not the routine exited successfully

message

str

description of the cause of termination

nfev

int

number of function evaluations

niter

int

number of optimization iterations

fun

float

value of objective function at x

Ah

float

max capacity (Ah) of the fitted cell dataset

x

1D array

the solution of the optimization

x_std

1D array

approximate standard deviation for each x

x_map

list[str]

names/order of values store in x

copy()#

Returns a copy of the instance.

Returns:

result (Self) – A deep copy of the current instance. Does not share any memory with the original instance.

class ampworks.dqdv.DqdvFitTable(extra_cols=None)[source]#

Container for many dQdV fits.

A container to store multiple dQdV fits. An instance of this class is required to run calc_lam_lli. Loop over multiple datasets and append fits one at a time to the table using the append method.

Parameters:

extra_cols (list[str] or None, optional) – Any extra, non-required columns to add to the table. Pass the column names and their row values to append when writing each row. Use to track equivalent full cycles or other metrics with each fit.

Raises:

TypeError – ‘extra_cols’ must be type list[str].

append(fit_result, **extra_cols)[source]#

Append a new row to the table.

Parameters:
  • fit_result (DqdvFitResult) – A result from the DqdvFitter's grid search or constrained fit.

  • extra_cols (dict, optional) – Any extra column names/values to include in the row.

Raises:

ValueError – Columns cannot be created on the fly. Any extra columns must be defined at initialization.

See also

DqdvFitResult

Container for a single dQdV fit.

copy()#

Returns a copy of the instance.

Returns:

table (Self) – A deep copy of the instance. Does not share memory with original.

classmethod from_csv(path)[source]#

Create a new instance from a CSV file.

Parameters:

path (str or Path) – Path to the CSV file.

Returns:

table (Self) – A new instance initialized with data from the file.

to_csv(path)#

Write the table to a CSV file.

Parameters:

path (str or Path) – Path to the output file.

property df: pandas.DataFrame#

The underlying DataFrame stored in the container.

class ampworks.dqdv.DqdvFitter(neg=None, pos=None, cell=None, cost_terms='all')[source]#

Wrapper for dQdV fitting.

Fits electrode stoichiometries to replicate the voltage response of a full cell. Fitting relies on low-rate half-cell and full-cell charge and/or discharge curves to approximate open-circuit potentials.

Internally, data is automatically used to generate smooth splines for the fitting routines. However, pre-processing to reduce noise in the measurements is recommended to avoid instabilities in the fits and derivative calculations.

Note that reported stoichiometries x0/x1 are in reference to relative states of charge. Furthermore, the values differ in meaning between the negative and positive electrodes. xp0 and xp1 refer to the relative measure of how delithiated the positive electrode is, whereas xn0 and xn1 refer to how lithiated the negative electrode is. This concention is used so that x0 < x1 in both electrodes. Furthermore, it allows x0 states to both refer to the SOC=0 state of the full cell and both x1 values to the SOC=1 state of the full cell.

Parameters:
  • neg (pd.DataFrame) – Negative electrode OCV data.

  • pos (pd.DataFrame) – Positive electrode OCV data.

  • cell (pd.DataFrame) – Full cell OCV data.

  • cost_terms (str or list[str], optional) – Error terms for optimization. ‘all’ (default) = [‘voltage’, ‘dqdv’, ‘dvdq’]. Accepts a string (single term) or list (subset of terms).

Notes

  • The dataframe inputs are all required. The default None values allow you to initialize the class first and add them one at a time. This is primarily to support interactions with the GUI.

  • When ‘voltage’ is included in cost_terms, an iR term is fit in addition to the x0/x1 stoichiometries. Otherwise, the ohmic iR offset is forced to 0. cost_terms can be modified after initialization via its property.

constrained_fit(x0, bounds=0.1, xtol=1e-08, maxiter=100000, return_full=False)[source]#

Run a trust-constrained local optimization routine to minimize error between the fit and data.

Parameters:
  • x0 (ArrayLike, shape(n,)) – Initial xn0, xn1, xp0, xp1, and optionally iR. If you already ran a previous fit you can simply use fit_result.x.

  • bounds (float or list[float], optional) – Symmetric parameter bounds (excludes iR). A float (default=0.1) applies to all. Use lists for per-x values. See notes for more info.

  • xtol (float, optional) – Convergence tolerance for parameters. Defaults to 1e-8.

  • maxiter (int, optional) – Maximum number of iteraterations. Defaults to 1e5.

  • return_full (bool, optional) – If True, include the complete OptimizeResult from SciPy in the output. Defaults to False.

Returns:

  • fit_result (DqdvFitResult) – A subset summary of SciPy’s optimization results, including an added approximate standard deviation for the pameters.

  • opt_result (OptimizeResult) – Full result form SciPy. Does not include standard deviation info. Only returned if return_full=True.

Notes

Bound indices correspond to xn0, xn1, xp0, and xp1, where 0 and 1 are in reference to lower and upper stoichiometries of the negative (n) and positive (p) electrodes. Set bounds[i] = 1 to disable bounds and use the full interval [0, 1] for x[i]. If an x[i] +/- bounds[i] exceeds [0, 1], the lower and/or upper bounds will be corrected to 0 and/or 1, respectively. Furthermore, bounds are clipped to be between 0.001 and 1 behind the scenes. It does not help to use values outside this range.

The fit_result output contains uncertainty estimates for the fitted parameters. These are approximated from the numerical Hessian at the optimum. The method assumes the function is locally linear, the input errors are independent and small, and the fit is well-behaved. Notes on the method are available here. These bounds provide more of a heuristic interpretation of the confidence intervals rather than a statistical interpretation. This is because all fitting routines use a mean absolute percent error (MAPE) function, but the uncertainty approximation needs a sum of squared residuals error function.

Since two different error functions are used between the fit and the uncertainty estimates, they are not directly linked. However, using the combination of these two functions has empirically provided consistent convergence and reasonable uncertainties across a broad range of data sets. We highlight the details of the methods here and leave it to the user to interpret and decide whether or not to belience and/or use the uncertainty estimates.

err_terms(params)[source]#

Calculate errors between the fit and data.

Parameters:

params (ArrayLike, shape(n,)) – Array for xn0, xn1, xp0, xp1, and iR (optional). If you already performed a fit you can simply use fit_result.x.

Returns:

errs (RichResult) – Voltage, dqdv, and dvdq errors. soc, fit, and data arrays are also included for convenience and plotting.

Notes

Errors are calculated as mean absolute percent errors between the data and fits. The normalization reduces preferences to fit any one cost term over others when more than one is considered. It also removes any units so it is more mathematically correct to sum the errors.

get_dqdv(which, soc)[source]#

Evaluate a dqdv spline.

Parameters:
  • which ({'neg', 'pos', 'cell'}) – Which dqdv spline to evaluate.

  • soc (ArrayLike) – Relative state-of-charge values, between [0, 1], to evaluate at. See notes for more information.

Returns:

dvdq (np.ndarray) – Evaluated dqdv values.

Raises:
  • ValueError – ‘which’ must be in [‘neg’, ‘pos’, ‘cell’].

  • RuntimeError – If the requested spline has not yet been constructed.

Notes

The individual electrode datasets and splines are internally stored in opposite directions of one another. See the get_ocv method for more information to ensure you are evaluating each in the correct directions.

get_dvdq(which, soc)[source]#

Evaluate a dvdq spline.

Parameters:
  • which ({'neg', 'pos', 'cell'}) – Which dvdq spline to evaluate.

  • soc (ArrayLike) – Relative state-of-charge values, between [0, 1], to evaluate at. See notes for more information.

Returns:

dvdq (np.ndarray) – Evaluated dvdq values.

Raises:
  • ValueError – ‘which’ must be in [‘neg’, ‘pos’, ‘cell’].

  • RuntimeError – If the requested spline has not yet been constructed.

Notes

The individual electrode datasets and splines are internally stored in opposite directions of one another. See the get_ocv method for more information to ensure you are evaluating each in the correct directions.

get_ocv(which, soc)[source]#

Evaluate an OCV spline.

Parameters:
  • which ({'neg', 'pos', 'cell'}) – Which OCV spline to evaluate.

  • soc (ArrayLike) – Relative state-of-charge values, between [0, 1], to evaluate at. See notes for more information.

Returns:

ocv (np.ndarray) – Evaluated OCV values.

Raises:
  • ValueError – ‘which’ must be in [‘neg’, ‘pos’, ‘cell’].

  • RuntimeError – If the requested spline has not yet been constructed.

Notes

Due to the internally storage of the half-cell data and splines, the OCV curves returned by this method are in opposite orders. Plotting the full [0, 1] window for the negative electrode will provide a curve with decreasing voltage from zero to 1 because x0/x1 refer to the state of how lithiated the negative electrode is. In contrast, the positive electrode OCV will return a curve with increasing potential because the fitted x0/x1 are in reference to how delithiated the positive electrode is. Evaluating the full cell curve also provides a curve with increasing voltage because the input is inpretted as the state of charge for the full cell. Values returned by all fitting routines are consistent with this orientation, and users can access the voltage windows of individual electrode potentials by passing the appropriate sections of the fitted x0/x1 values. For example, use get_ocv(fit_result.x[0:2]) for the negative electrode and get_ocv(fit_result[2:4]) for the positive electrode.

Determine the minimum error by evaluating parameter sets taken from intersections of a coarse grid. Parameter sets where either x0 < x1 are ignored.

Parameters:

Nx (int) – Number of discretizations between [0, 1] for each parameter.

Returns:

fit_result (DqdvFitResult) – Summarized results from the grid search.

plot(params, return_axs=False)[source]#

Plot the model fit vs. data.

Parameters:
  • params (ArrayLike, shape(n,)) – Array for xn0, xn1, xp0, xp1, and iR (optional). If you already performed a fit you can simply use fit_result.x.

  • return_axs (bool, optional) – If True (default), return the axes objects for the voltage, dqdv, and dvdq. Otherwise, returns None.

Returns:

axes (list[plt.Axes] or None) – List of four axes objects for the voltage, dqdv, and dvdq plots. The voltage plot has two y axes and consequently two axes objects. Only returned if return_axs=True, otherwise None.

property cell: pandas.DataFrame#

Get or set the full cell dataframe.

Columns must include ‘Ah’ for capacity and ‘Volts’ for the full-cell voltage. All ‘Ah’ values must be positive, and there must be a zero reference somewhere in the column.

property cost_terms: list[str]#

Get or set which terms are included in the constrained fit’s cost function. Options are ‘voltage’, ‘dqdv’, and/or ‘dvdq’. You can also set to ‘all’ to conveniently select all three cost terms.

property neg: pandas.DataFrame#

Get or set the negative electrode dataframe.

Columns must include ‘Ah’ for capacity and ‘Volts’ for the half-cell voltage. All ‘Ah’ values must be positive, and there must be a zero reference somewhere in the column.

property pos: pandas.DataFrame#

Get or set the positive electrode dataframe.

Columns must include ‘Ah’ for capacity and ‘Volts’ for the half-cell voltage. All ‘Ah’ values must be positive, and there must be a zero reference somewhere in the column.

class ampworks.dqdv.DqdvSpline(capacity_method='auto')[source]#

Smoothing spline for dQdV curves.

A class for fitting and evaluating smoothing splines for dQdV data. Use the fit method to fit splines to datasets. The API takes inspiration from scikit-learn’s estimator interface. Instance variables (see list below) and all methods ending with an underscore (e.g., volts_(soc), score_) are only available after fitting.

The smoothing spline is only applied to the voltage vs. state of charge (SOC) data. dVdQ and dQdV curves are then computed from the derivative, and its inverse, of the voltage spline. All splines are parameterized by SOC, including dQdV. So to plot dQdV vs. voltage, first evaluate the voltage spline at the desired SOC values, then evaluate the dQdV spline at the same SOC values.

Parameters:

capacity_method ({'auto', 'provided', 'integrated'}, optional) – How capacity is determined. ‘auto’ (default) uses the ‘Ah’ column if present, otherwise integrates current over time. ‘provided’ requires and uses ‘Ah’, throwing errors if missing. ‘integrated’ forces the integration of current and ignores ‘Ah’.

Variables:
  • Ah_ (1D np.array) – Stored capacities used in constructing the spline.

  • SOC_ (1D np.array) – Stored state of charge values used in constructing the spline.

  • Volts_ (1D np.array) – Stored voltage values used in constructing the spline, not to be confused with the spline itself, evaluated using volts_().

  • score_ (float) – Root mean square error between smoothed and raw voltages.

dqdv_(soc)[source]#

Evaluate the dQdV spline at given SOC values.

Parameters:

soc (ArrayLike) – State of charge values at which to evaluate the dQdV spline.

Returns:

dvdq (ArrayLike) – dQdV values corresponding to the given SOC values. The output units are 1/Volts instead of Ah/Volts, since splines are fit using SOC.

Raises:

RuntimeError – Call ‘fit’ before evaluating.

dvdq_(soc)[source]#

Evaluate the dVdQ spline at given SOC values.

Parameters:

soc (ArrayLike) – State of charge values at which to evaluate the dVdQ spline.

Returns:

dvdq (ArrayLike) – dVdQ values corresponding to the given SOC values. The output units are Volts instead of Volts per Ah, since splines are fit using SOC.

Raises:

RuntimeError – Call ‘fit’ before evaluating.

fit(data, s=0.0)[source]#

Fit a smoothing spline to voltage vs. SOC data, and use the spline to construct dVdQ and dQdV splines using the derivative and its inverse.

Parameters:
  • data (amp.Dataset) – Sliced charge or discharge data to fit a spline to. Must contain, columns for {'Ah', 'Volts'} or {'Seconds', 'Amps', 'Volts'} depending on capacity_method. See notes for more information.

  • s (float, optional) –

    The smoothing condition passed to SciPy’s make_splrep. Controls the trade-off between closeness to the data and smoothness of fit according to

    sum( (g(x) - y)**2 ) <= s
    

    By default, s=0., which results in an interpolating spline. Using a larger value produces a smoother spline. If you’re unsure, start with a small value like s=1e-4 and increase/decrease, as needed.

Returns:

spline (Self) – The fitted DqdvSpline object.

Raises:
  • ValueError – Missing required columns in ‘data’.

  • ValueError – Charge and discharge data must have positive and negative current, respectively. See notes for more information.

  • ValueError – Invalid ‘Ah’ column: minimum value is not zero and/or values are not monotonically increasing.

Notes

This method expects charge/discharge currencts to be positive/negative, respectively. If your sign convention is different, you will need to adjust the current data accordingly before fitting. Otherwise, internal checks will raise a ValueError. The sign convention does not change for half cells vs. full cells. Thus, NMC cathode half cells, graphite anode half cells, or any full cell should all have positive current when voltage is increasing (charging) and negative current when voltage is decreasing (discharging).

The capacity_method parameter controls how capacity is determined and which required columns are expected in data. Capacity can either be taken directly from an ‘Ah’ column, or calculated by integrating current over time. If capacity_method='provided', then the minimum required columns are {‘Ah’, ‘Volts’}. If capacity_method='integrated', then the minimum required columns are {‘Seconds’, ‘Amps’, ‘Volts’}. Using the default capacity_method='auto' will accept either of these options, preferring the ‘Ah’ column if it is present, and integrating otherwise.

In cases where an ‘Ah’ column is provided and used, the values will be checked to ensure they are valid. The minimum value must be zero and values must be monotonically increasing. Make sure your capacity column meets these requirements if you choose to use it.

plot(return_axs=False)[source]#

Plot the fitted splines against the original data.

Returns:

axes (np.ndarray[plt.Axes] or None) – A 2x2 axes object containing the plots. This is only returned if return_axs=True, otherwise None.

volts_(soc)[source]#

Evaluate the voltage spline at given SOC values.

Parameters:

soc (ArrayLike) – State of charge values at which to evaluate the voltage spline.

Returns:

voltage (ArrayLike) – Voltage values corresponding to the given SOC values.

Raises:

RuntimeError – Call ‘fit’ before evaluating.

ampworks.dqdv.calc_lam_lli(fit_table)[source]#

Calculate degradation modes.

Uses full cell capacity and fitted x0/x1 values from dqdv/dvdq fits to calculate theoretical electrode capacities, loss of active material (LAM), and loss of lithium inventory (LLI). The calculations are summarized below. For more detail and discussion, please refer to [1].

Electrode capacities (Q) and loss of active material (LAM) are

\[Q_{ed} = \frac{\rm capacity}{x_{1,ed} - x_{0,ed}}, \quad \quad {\rm LAM}_{ed} = 1 - \frac{Q_{ed}}{Q_{ed}[0]},\]

where \(ed\) is generic for ‘electrode’. Outputs use ‘n’ and ‘p’ to differentiate between negative and positive electrodes, respectively. Loss of lithium inventory (LLI) is

\[{\rm Inv} = x_{0,n}Q_{n} + (1 - x_{0,p})Q_{p}, \quad \quad {\rm LLI} = 1 - \frac{\rm Inv}{\rm Inv[0]},\]

where \({\rm Inv}\) is the total lithium inventories using capacities \(Q\) from above. If standard deviations of the x0/x1 stoichiometries are available in results (and are not NaN), then they are propagated to give uncertainty estimates for the LAM/LLI values. Reported uncertainties come from first-order Taylor series assumptions. If you trust your x0/x1 fits but see large or inconsistent uncertainties then it is also safe to trust the LAM/LLI values, but you may want to neglect LAM/LLI uncertainties. Note that (1 - xp0) is used instead of just xp0 because x0 refers to the delithiated state of the positive electrode whereas xn0 refers to the lithiated state of the negative electrode, and does not require the same inversion.

Parameters:

fit_table (DqdvFitTable) – Table containing rows for fitted x0/x1 values from dqdv/dvdq fits.

Returns:

deg_table (DegModeTable) – Electrode capacities (Q) and loss of active material (LAM) for the negative (n) and positive (p) electrodes, and loss of lithium inventory (LLI). Capacities are in Ah. All other outputs are unitless.

See also

DqdvFitter

Access to fitting routines.

DegModeTable

Table of calculated degradation modes.

References

ampworks.dqdv.plot_lam_lli(deg_table, x_col=None, std=False, return_axs=False)[source]#

Plot degradation modes.

Parameters:
  • deg_table (DegModeTable) – Container holding calculated degradation modes (LAM and LLI).

  • x_col (str | None, optional) – A column name from ‘fit_table` to use for the x-axis. If None (default) then the row indices are used.

  • std (bool, optional) – Include shaded regions for estimated standard deviations of the LAM and LLI values when True. Default is False.

  • return_axs (bool, optional) – If True (default), return the axes objects for the capacity, LAM, and LLI plots. Otherwise, returns None.

Returns:

axes (np.ndarray[plt.Axes] or None) – A 2x3 axes object containing the capacity, LAM, and LLI plots. This is only returned if return_axs=True, otherwise None.

See also

DqdvFitter

Access to the fitting routines.

DegModeTable

Table of calculated degradation modes.

calc_lam_lli

Calculate degradation modes before plottting.

ampworks.dqdv.run_gui(jupyter_mode='external', jupyter_height=650)[source]#

Run a graphical dQdV fitting interface.

Parameters:
  • jupyter_mode ({'external', 'inline'}, optional) – How to display the GUI in jupyter notebooks. Run in a new browser tab with ‘external’ (default), or in the notebook with ‘inline’.

  • jupyter_height (int, optional) – Height (in px) when displayed using ‘inline’. Defaults to 650.

Warning

This function is only intended for use inside Jupyter Notebooks. You may experience issues if you call it from a normal script, or in an interactive session within some IDEs (e.g., Spyder, PyCharm, IPython, etc.). if you’re looking for another way to access the GUI without needing to open Jupyter Notebooks, you can use the ampworks --app command from your terminal.