Validation

Interpreting EIS data fundamentally relies on the the system conforming to conditions of causality, linearity, and stability. For an example of how the adherence to the Kramers-Kronig relations, see the Validation Example Jupyter Notebook

Lin-KK method

Validating your data with the lin-KK model requires fitting an optimal number of RC-elements and analysis of the residual errors.

impedance.validation.calc_mu(Rs)[source]

Calculates mu for use in LinKK

impedance.validation.eval_linKK(elements, ts, f)[source]

Builds a circuit of RC elements to be used in LinKK

impedance.validation.fit_linKK(f, ts, M, Z, fit_type='real', add_cap=False)[source]

Fits the linKK model using linear regression

Parameters:
f: np.ndarray

measured frequencies

ts: np.ndarray

logarithmically spaced time constants of RC elements

M: int

the number of RC elements

Z: np.ndarray of complex numbers

measured impedances

fit_type: str

selects which components of data are fit (‘real’, ‘imag’, or ‘complex’)

add_cap: bool

option to add a serial capacitance that helps validate data with no low-frequency intercept

Returns:
elements: np.ndarray

values of fit \(R_k\) in RC elements and series \(R_0\), L, and optionally C.

mu: np.float

under- or over-fitting measure

Notes

Since we have a system of equations, \(Ax ~= b\), that’s linear wrt \(R_k\), we can fit the model by calculating the pseudo-inverse of A. \(Ax\) is our model fit, \(\hat{Z}\), and \(b\) is the normalized real or imaginary component of the impedance data, \(Re(Z)/|Z|\) or \(Im(Z)/|Z|\), respectively.

\(\hat{Z} = R_0 + \sum^M_{k=1}(R_k / |Z|(1 + j * w * \tau_k))\). \(x\) is an (M+1) \(\times\) 1 matrix where the first row contains \(R_0\) and subsequent rows contain \(R_k\) values. A is an N \(\times\) (M+1) matrix, where N is the number of data points, and M is the number of RC elements.

Examples

Fitting the real part of data, the first column of A contains values of \(\frac{1}{|Z|}\), the second column contains \(Re(1 / |Z| (1 + j * w * \tau_1))\), the third contains \(Re(1 / |Z| (1 + j * w * \tau_2))\) and so on. The \(R_k\) values within the x matrix are found using numpy.linalg.pinv when fit_type = ‘real’ or ‘imag’. When fit_type = ‘complex’ the coefficients are found “manually” using \(r = ||A'x - b'||^2 + ||A''x - b'||^2\) according to Eq 14 of Schonleber [1].

[1] Schönleber, M. et al. A Method for Improving the Robustness of linear Kramers-Kronig Validity Tests. Electrochimica Acta 131, 20–27 (2014) doi: 10.1016/j.electacta.2014.01.034.

impedance.validation.get_tc_distribution(f, M)[source]

Returns the distribution of time constants for the linKK method

impedance.validation.linKK(f, Z, c=0.85, max_M=50, fit_type='real', add_cap=False)[source]

A method for implementing the Lin-KK test for validating linearity [1]

Parameters:
f: np.ndarray

measured frequencies

Z: np.ndarray of complex numbers

measured impedances

c: np.float

cutoff for mu

max_M: int

the maximum number of RC elements

fit_type: str

selects which components of data are fit (‘real’, ‘imag’, or ‘complex’)

add_cap: bool

option to add a serial capacitance that helps validate data with no low-frequency intercept

Returns:
M: int

number of RC elements used

mu: np.float

under- or over-fitting measure

Z_fit: np.ndarray of complex numbers

impedance of fit at input frequencies

resids_real: np.ndarray

real component of the residuals of the fit at input frequencies

resids_imag: np.ndarray

imaginary component of the residuals of the fit at input frequencies

Notes

The lin-KK method from Schönleber et al. [1] is a quick test for checking the validity of EIS data. The validity of an impedance spectrum is analyzed by its reproducibility by a Kramers-Kronig (KK) compliant equivalent circuit. In particular, the model used in the lin-KK test is an ohmic resistor, \(R_{Ohm}\), and \(M\) RC elements.

\[\hat Z = R_{Ohm} + \sum_{k=1}^{M} \frac{R_k}{1 + j \omega \tau_k}\]

The \(M\) time constants, \(\tau_k\), are distributed logarithmically,

\[\tau_1 = \frac{1}{\omega_{max}} ; \tau_M = \frac{1}{\omega_{min}} ; \tau_k = 10^{\log{(\tau_{min}) + \frac{k-1}{M-1}\log{{( \frac{\tau_{max}}{\tau_{min}}}})}}\]

and are not fit during the test (only \(R_{Ohm}\) and \(R_{k}\) are free parameters).

In order to prevent under- or over-fitting, Schönleber et al. propose using the ratio of positive resistor mass to negative resistor mass as a metric for finding the optimal number of RC elements.

\[\mu = 1 - \frac{\sum_{R_k \ge 0} |R_k|}{\sum_{R_k < 0} |R_k|}\]

The argument c defines the cutoff value for \(\mu\). The algorithm starts at M = 3 and iterates up to max_M until a \(\mu < c\) is reached. The default of 0.85 is simply a heuristic value based off of the experience of Schönleber et al., but a lower value may give better results.

If the argument c is None, then the automatic determination of RC elements is turned off and the solution is calculated for max_M RC elements. This manual mode should be used with caution as under- and over-fitting should be avoided.

[1] Schönleber, M. et al. A Method for Improving the Robustness of linear Kramers-Kronig Validity Tests. Electrochimica Acta 131, 20–27 (2014) doi: 10.1016/j.electacta.2014.01.034.

impedance.validation.residuals_linKK(elements, ts, Z, f, residuals='real')[source]

Calculates the residual between the data and a LinKK fit