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()
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()
[ ]: