Detailed example, fitting with a model equivalent circuit

This example demonstrates how to use the EZ module to fit an equivalent circuit to an impedance vs angular frequency response, measured experimentally at different applied bias. In this example electrochemical impedance spectroscopy (EIS) data is analyzed.

Equivalent circuit definition

An equivalent circuit can be defined using the classes R, C, Q and W. R correspond to a resistance, C a capacitance, Q a constant phase element and W a Warburg element. In this example we only use R and Q. We initialize all the elements in the circuit with a unique label:

from EZ.model import Q, R

R_sol = R("S")
R_b = R("Bulk")
Q_b = Q("SC")
R_s = R("SS")
Q_s = Q("SS")

Then we can define a model equivalent circuit using these elements. Adding elements is equivalent to connecting them in series and dividing elements is equivalent to connecting them in parallel. Blocks of elements can be constituted using parentheses.

model = R_sol + Q_b/(R_b + Q_s/R_s)

The circuit can be displayed using its print method:

model.print()
_images/EIS_9_0.svg

Evaluation of the equivalent circuit impedance vs angular frequency behavior

The EZ module allows to evaluate an equivalent circuit impedance vs angular frequency characteristics. To this mean we need to initialize the parameters of the elements (R and Q) defined above. Depending on the kind of element a different number of parameter has to be initialized. A resistance for example has one parameter, its resistance value, named with “R_” and the label of the considered element. A constant phase element has two parameters, a pseudo-capacitance (“Q_” + label) and a non-ideality factor (“n_” + label). All the parameters are initialized using a dictionary whose keys are the parameter name and whose values are dictionnaries holding the variables used for initialization. These variables have to contain a value and can also contain additional information on the parameter that will be used at the fitting step.

pars = {
    "R_S":    dict( value = 0.025, vary = False ),
    "R_Bulk": dict( value = 10,    min = 0      ),
    "R_SS":   dict( value = 50,    min = 0      ),
    "Q_SC":   dict( value = 1e-3,  min = 0      ),
    "Q_SS":   dict( value = 1e-2,  min = 0      ),
    "n_SC":   dict( value = 0.9,   vary = False ),
    "n_SS":   dict( value = 0.8,   vary = False )
}

The model impedance vs angular frequency is evaluated and displayed in Bode and Nyquist plots using its plot method. A range of frequencies can be passed via the range_omega argument. Moreover, a list of additional circuits can be passed via the partial_models argument. It allows to vizualise the contribution of some components to the overall impedance characteristic. These models impedances are overlayed in the Bode plots. In the Nyquist plot the regions where a partial circuit impedance absolute value is larger is highlighted with a corresponding color. Here for example we use this visualization to show the parts of the circuit influencing respectively the low and high frequencies responses, corresponding respectively to the surface and bulk of an electrode.

model.plot(
    partial_models=[Q_b/R_b, Q_s/R_s],
    pars=pars,
    range_omega = [1e-2, 1e4]
)
_images/EIS_16_0.svg

Loading and plotting the EIS data

Data loading, plotting and fitting is done using an object of class Dataset. This object initialization requires at least the path to the folder where the files are stored. To be loaded these files should be formatted properly. The files used in this example can be found here, and the details on how to format the files for proper loading are documented here. Optional arguments passed in this example are the pH to convert to RHE and electrode area to normalize the impedance.

from EZ.data import Dataset

ds = Dataset(
    folder="data/EIS CFO pH14 light",
    pH=14,
    area=0.25
)

In this example we recorded the impedance at frequencies up to 10 MHz. Since there is no relevant impedance trend above 10 kHz change the dataset range of frequencies using the set_freq_range method. Then the dataset is plotted using the plot method where the data is represented as full circles.

ds.set_freq_range([1e-10, 1e4])
ds.plot()
_images/EIS_22_0.svg

Fitting and exporting fit results

The fit is performed using the fit method. This method requires two arguments, the model used for the fit, defined here as an equivalent circuit, and a dictionary setting the model parameters initial guess and constraints. In this dictionnary, declared previously, we fixed some parameters (R_S, n_SC and n_SS) by setting the variable vary to False. We also set the remaining parameters to be positive by setting the variable min to 0. Maximum values could be used also using the variable max.

ds.fit(model, pars=pars)

Once the fit is performed, using the plot method also displays an evaluation of the fit as a full line of the same color as the corresponding data:

ds.plot()
_images/EIS_27_0.svg

The raw data and corresponding fit can be exported using the export method. The resulting exported files for this example can be consulted here.

ds.export()

The parameters fitted value and standard error can be exported using the export_result method. The resulting exported files for this example can be consulted here. Passing the argument show=True to this method also displays these values as shown below.

ds.export_result(show=True)
value (fixed)
R_S 0.025
n_SC 0.900
n_SS 0.800
E [V vs RHE] Q_SC Q_SC std R_Bulk R_Bulk std Q_SS Q_SS std R_SS R_SS std
0.725 0.00226 2.18e-05 13.4 0.151 0.0132 0.000166 26.1 0.149
0.745 0.00213 3.02e-05 13.7 0.199 0.0142 0.000215 30.5 0.206
0.765 0.00208 3.51e-05 14.1 0.219 0.0154 0.000235 35.3 0.248
0.785 0.00204 3.73e-05 13.5 0.21 0.0155 0.000192 43.6 0.277
0.805 0.00207 6.17e-05 13 0.317 0.0159 0.000264 50.1 0.48
0.825 0.00214 4.25e-05 13.7 0.209 0.0174 0.000173 60 0.424
0.845 0.00224 5.91e-05 13.2 0.264 0.0181 0.000222 63.1 0.602
0.865 0.00231 4.66e-05 12.6 0.189 0.0187 0.000156 69.2 0.513
0.885 0.00252 4.85e-05 13.1 0.187 0.0207 0.000176 70.4 0.595
0.905 0.00277 5.82e-05 13.1 0.205 0.0228 0.000217 72.5 0.783
0.925 0.00294 7.19e-05 11.8 0.217 0.0223 0.000203 80.2 0.925
0.945 0.00351 6.53e-05 12 0.179 0.0244 0.000182 81.4 0.848
0.965 0.0041 6.22e-05 10.7 0.14 0.0233 0.00011 102 0.83
0.985 0.0055 6.83e-05 10.8 0.132 0.0258 0.000109 106 0.913
1.005 0.00775 9.38e-05 9.88 0.142 0.0277 0.000117 107 0.969
1.025 0.0102 0.000134 9.58 0.171 0.031 0.000163 97.5 1.11
1.045 0.0112 0.000179 9.15 0.2 0.0338 0.000219 94.9 1.44
1.065 0.0113 0.000146 8.69 0.148 0.0361 0.000191 87.1 1.06
1.085 0.0111 0.000146 8.29 0.136 0.0376 0.000191 91.1 1.19
1.105 0.0109 0.000137 8.12 0.119 0.0408 0.000203 87.1 1.18
1.125 0.0108 0.000296 7.79 0.225 0.0481 0.000571 74.2 2.42