ECSHackWeek / impedance.py

A Python package for working with electrochemical impedance data
MIT License
212 stars 102 forks source link

Initial guesses #197

Closed dinisaurier closed 2 years ago

dinisaurier commented 2 years ago

Hi there,

I have my data (see attached) which I can plot but I have no idea what initial guesses to make. I don't get a fit right now (I am only using the initial guesses from the getting started guide). How does it work to guess the parameters?

Still new to Python + impedance data :(

Thanks a lot.

exampleData2.csv example_fit_fig

BGerwe commented 2 years ago

Hi,

Welcome to Python and the impedance community!

Interpreting impedance data is not trivial, but there are great resources available for learning. Some excellent learning resources are:

Now, to your original question. Choosing initial parameters depends a lot on the equivalent circuit model that is being fit. So from the shape of this curve you might guess there is a resistance (probably from an electrolyte) in series with an RC element and a Warburg diffusion element. You define that model with a representative string:

circuit_str = 'R_0-p(R_1,C_1)-W_1'

Then, initial guesses are passed with a list where each position corresponds to an equivalent circuit model element. So, the first list element is for R_0, the second element is for R_1, and so on. Looking at the impedance data, the high-frequency intercept is around 0.0025 Ohms, so we'll guess that for R_0. Next, the RC-like arc has a width ~0.035 Ohms (~0.0375 - ~0.0025), so we'll assign that to R_1. To estimate capacitance we need the peak frequency of the arc. Assuming it's ~200 Hz, that gives us ~0.023 F (the RC section of this post explains that calculation). Finally, seeing the ~45° tail extends about 0.005 Ohm over roughly a decade in frequency (assuming 0.1 to 0.01 Hz), we can guess A_w is ~0.002 Ohm s^(-1/2) .

So we define those with:

init_guess = [.0025, .035, .023, .002]

And finally, define our circuit:

circuit = CustomCircuit(initial_guess=init_guess, circuit=circuit_str)

I'm not saying the circuit proposed above will fit the data well. In fact, I suspect it will be a fairly poor fit, but I wanted to walk through an example of looking at the spectrum, proposing an equivalent circuit model, and estimating initial parameters.

I hope that helps!

dinisaurier commented 2 years ago

Hey, thank you so much for your patience! Your information help a lot with understanding!

I tried your proposed circuit though and still did not get any fit (see attached pic).

example_fit_fig

BGerwe commented 2 years ago

Would you mind sharing the code you're using? That will help me understand better. And just as an FYI, it's good practice to include code for a minimum working example when asking for help. 🙂

dinisaurier commented 2 years ago

Hey, I am sorry, here is the code. It's the exact code from the website (first example), only with your changes. I Initially kept the circuit the example used cuz the data come from a lithium-ion battery (and when I did my reasearches correct, the circuit R0-p(R1,C1)-p(R2-W1,C2) is supposed to fit(?) ^^"

from impedance import preprocessing

frequencies, Z = preprocessing.readCSV('./exampleData2.csv')

frequencies, Z = preprocessing.ignoreBelowX(frequencies, Z)

from impedance.models.circuits import CustomCircuit

circuit_str = 'R_0-p(R_1,C_1)-W_1' init_guess = [.0025, .035, .023, .002] circuit = CustomCircuit(initial_guess=init_guess, circuit=circuit_str)

circuit.fit(frequencies, Z)

print(circuit)

Z_fit = circuit.predict(frequencies)

import matplotlib.pyplot as plt from impedance.visualization import plot_nyquist

fig, ax = plt.subplots() plot_nyquist(ax, Z, fmt='o') plot_nyquist(ax, Z_fit, fmt='-')

plt.legend(['Data', 'Fit']) plt.show()

BGerwe commented 2 years ago

I think I've figured out what's going on here. It seems like there are some problems with the frequencies in exampleData2.csv. On the complex plane (Nyquist plot), the impedance looks OK, but on a Bode plot we see many of the frequencies are unreasonably high, and jump all over the place. image

This seems to be why neither model fits.

Looking through the data, it seems like the frequency range should be 10 kHz to 10 mHz (but there's also some positive Z'' data appended at the end, which are removed by preprocessing.ignoreBelowX()). Creating that frequency list with frequencies = np.logspace(4, -2, num=61)[5:] we get much more reasonable fits.

For the model I proposed above:

circuit_str = 'R_0-p(R_1,C_1)-W_1'
init_guess = [.0025, .035, .023, .002]

image

And for the model in the getting started guide:

circuit = 'R0-p(R1,C1)-p(R2-Wo1,C2)'
initial_guess = [.01, .01, 100, .01, .05, 100, 1]

image

In the future, one thing you can do to check if you're data is legitimate or not is perform a Kramers-Kronig test. You can do that in this package with:

M, mu, Zfit, resids_real, resids_imag = validation.linKK(frequencies, Z)

Looking at the residuals from the original data immediately shows there are problems (But the problems are also evident from the Bode plot I posted above.): image

With the assumed frequency range I used above, the residuals are: image Which is reasonable, though even this suggests some problems in the data... I suggest reading our documentation for Validation of EIS data and the cited references for more discussion on the topic.

dinisaurier commented 2 years ago

Wow, you are such a blessing. Thank you so much for your effort!

BGerwe commented 2 years ago

You're very welcome!

When you feel that your questions have been answered would you mind closing this issue?