Fitting impedance spectra

1. Import and initialize equivalent circuit(s)

To begin we will import the Randles’ circuit and a custom circuit from the impedance package. A full list of currently available circuits are available in the documentation.

[1]:
import sys
sys.path.append('../../../')

from impedance.circuits import Randles, CustomCircuit

The classes we just imported represent different equivalent circuit models. To actually use them we want to initialize a specific instance and provide an initial guess for the parameters and any other options.

E.g. for the randles circuit, one of the options is for a constant phase element (CPE) instead of an ideal capacitor.

[2]:
randles = Randles(initial_guess=[.01, .005, .1, .001, 200])
randlesCPE = Randles(initial_guess=[.01, .005, .1, .9, .001, 200], CPE=True)

Defining the custom circuit works a little differently. Here we pass a string comprised of the circuit elements grouped either in series (separated with a -) or in parallel (using the form p(X,Y)). Elements with multiple parameters are given in the form X/Y

[3]:
customCircuit = CustomCircuit(initial_guess=[.01, .005, .1, .005, .1, .001, 200],
                              circuit='R_0-p(R_1,C_1)-p(R_1,C_1)-W_1')

Each of the circuit objects we create can be printed in order to see the properties that have been defined for that circuit.

[4]:
print(randles)

-------------------------------
Circuit: Randles
Circuit string: R0-p(R1,C1)-W1
Fit: False

-------------------------------
Initial guesses:
        R0 = 1.00e-02
        R1 = 5.00e-03
        C1 = 1.00e-01
        W1_0 = 1.00e-03
        W1_1 = 2.00e+02

2. Formulate data

Several convenience functions for importing data exist in the impedance.preprocessing module; however, here we will simply read in a .csv file containing frequencies as well as real and imaginary impedances using the numpy package.

[5]:
import numpy as np

data = np.genfromtxt('../../../data/exampleData.csv', delimiter=',')

frequencies = data[:,0]
Z = data[:,1] + 1j*data[:,2]

# keep only the impedance data in the first quandrant
frequencies = frequencies[np.imag(Z) < 0]
Z = Z[np.imag(Z) < 0]

3. Fit the equivalent circuits to a spectrum

Each of the circuit classes has a .fit() method which finds the best fitting parameters.

After fitting a circuit, the fit parameters rather that the inital guesses are shown when printing.

[6]:
randles.fit(frequencies, Z)
randlesCPE.fit(frequencies, Z)
customCircuit.fit(frequencies, Z)

print(customCircuit)

-------------------------------
Circuit: None
Circuit string: R_0-p(R_1,C_1)-p(R_1,C_1)-W_1
Fit: True

-------------------------------
Fit parameters:
        R_0 = 1.65e-02 +/- 1.54e-04
        R_1 = 5.31e-03 +/- 2.06e-04
        C_1 = 2.32e-01 +/- 1.90e-02
        R_1 = 8.77e-03 +/- 1.89e-04
        C_1 = 3.28e+00 +/- 1.85e-01
        W_1_0 = 6.37e-02 +/- 2.03e-03
        W_1_1 = 2.37e+02 +/- 1.72e+01

4a. Predict circuit model and visualize with matplotlib

[7]:
import matplotlib.pyplot as plt
from impedance.plotting import plot_nyquist

f_pred = np.logspace(5,-2)

randles_fit = randles.predict(f_pred)
randlesCPE_fit = randlesCPE.predict(f_pred)
customCircuit_fit = customCircuit.predict(f_pred)

fig, ax = plt.subplots(figsize=(5,5))

plot_nyquist(ax, frequencies, Z)
plot_nyquist(ax, f_pred, randles_fit, fmt='-')
plot_nyquist(ax, f_pred, randlesCPE_fit, fmt='-')
plot_nyquist(ax, f_pred, customCircuit_fit, fmt='-')

plt.show()
../_images/examples_fitting_example_16_0.png

4b. Or use the convenient plotting method included in the package

This is an experimental feature with many improvements (interactive plots, Bode plots, better confidence intervals) coming soon!!

[8]:
randles.plot(f_data=frequencies, Z_data=Z)
randlesCPE.plot(f_data=frequencies, Z_data=Z)
customCircuit.plot(f_data=frequencies, Z_data=Z)

plt.show()
../_images/examples_fitting_example_18_0.png
../_images/examples_fitting_example_18_1.png
../_images/examples_fitting_example_18_2.png
[ ]: