ECSHackWeek / impedance.py

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

[HELP][BUG?] EIS fitted circuit plot does not extent to the end. #261

Closed kevinsmia1939 closed 1 year ago

kevinsmia1939 commented 1 year ago

Describe the bug At high frequencies, the fitted plot does not extend all the way. As shown in the figure below, the orange line (fitted curve) does not extend as far as the data go. Is this normal?

To Reproduce

from impedance import preprocessing
from impedance.models.circuits import Randles, CustomCircuit
import matplotlib.pyplot as plt
import numpy as np

freq = np.array([100000.0, 79432.82, 63095.73, 50118.72, 39810.72, 31622.78, 25118.87, 19952.62, 15848.93, 12589.25, 10000.0, 7943.28, 6309.57, 5011.87, 3981.07, 3162.28, 2511.89, 1995.26, 1584.89, 1258.93, 1000.0, 794.33, 630.96, 501.19, 398.11, 316.23, 251.19, 199.53, 158.49, 125.89, 100.0, 79.43, 63.1, 50.12, 39.81, 31.62, 25.12, 19.95, 15.85, 12.59, 10.0])
z = np.array([(1.4+0.14j), (1.41+0.05j), (1.42+0.03j), (1.43+0j), (1.45-0.01j), (1.46-0.02j), (1.47-0.03j), (1.47-0.04j), (1.48-0.04j), (1.49-0.05j), (1.5-0.05j), (1.51-0.06j), (1.51-0.07j), (1.53-0.07j), (1.54-0.08j), (1.55-0.08j), (1.57-0.09j), (1.58-0.09j), (1.6-0.1j), (1.62-0.1j), (1.63-0.09j), (1.65-0.09j), (1.66-0.08j), (1.68-0.08j), (1.69-0.07j), (1.69-0.07j), (1.7-0.07j), (1.71-0.07j), (1.71-0.06j), (1.72-0.07j), (1.72-0.07j), (1.73-0.08j), (1.73-0.09j), (1.74-0.11j), (1.74-0.13j), (1.75-0.15j), (1.76-0.18j), (1.77-0.21j), (1.79-0.25j), (1.81-0.3j), (1.83-0.36j)])

circuit = CustomCircuit(initial_guess=[1.45, .27, 2.59e-03, 7.76e-01, 9.50e-02, 8.09e-01], circuit='R_0-p(R_1,CPE_1)-CPE_2')
freq, z = preprocessing.cropFrequencies(freq, z)
freq, z = preprocessing.ignoreBelowX(freq, z)
circuit.fit(freq, z)
z_fit = circuit.predict(freq)

fig, ax = plt.subplots(figsize=(10,10))
ax = circuit.plot(ax, f_data=freq, Z_data=z, kind='nyquist')

print(circuit)

Expected behavior I expect the fitted curve to extend as long as the data.

eis_ques

BGerwe commented 1 year ago

Hi @kevinsmia1939,

I copied your code and found that the highest frequency point from z_fit is 1.46670641-0.02215384j. For a closer look I zoomed the plot into the area of interest, which shows the fit is being plotted correctly. image

I think it's plotting correctly, but more an issue of fitting those high frequency points. You can try to get a better fit to those points by increasing their fitting weights. See this comment for more details.

kevinsmia1939 commented 1 year ago

Hi @BGerwe Thank for the suggestion I try weighting option that I can find. Using sigma and weight_by_modulus, but I got the same result.

I think I might be using it wrong, let me know what you think. Thanks.

from impedance import preprocessing
from impedance.models.circuits import Randles, CustomCircuit
import matplotlib.pyplot as plt
import numpy as np

freq = np.array([100000.0, 79432.82, 63095.73, 50118.72, 39810.72, 31622.78, 25118.87, 19952.62, 15848.93, 12589.25, 10000.0, 7943.28, 6309.57, 5011.87, 3981.07, 3162.28, 2511.89, 1995.26, 1584.89, 1258.93, 1000.0, 794.33, 630.96, 501.19, 398.11, 316.23, 251.19, 199.53, 158.49, 125.89, 100.0, 79.43, 63.1, 50.12, 39.81, 31.62, 25.12, 19.95, 15.85, 12.59, 10.0])
z = np.array([(1.4+0.14j), (1.41+0.05j), (1.42+0.03j), (1.43+0j), (1.45-0.01j), (1.46-0.02j), (1.47-0.03j), (1.47-0.04j), (1.48-0.04j), (1.49-0.05j), (1.5-0.05j), (1.51-0.06j), (1.51-0.07j), (1.53-0.07j), (1.54-0.08j), (1.55-0.08j), (1.57-0.09j), (1.58-0.09j), (1.6-0.1j), (1.62-0.1j), (1.63-0.09j), (1.65-0.09j), (1.66-0.08j), (1.68-0.08j), (1.69-0.07j), (1.69-0.07j), (1.7-0.07j), (1.71-0.07j), (1.71-0.06j), (1.72-0.07j), (1.72-0.07j), (1.73-0.08j), (1.73-0.09j), (1.74-0.11j), (1.74-0.13j), (1.75-0.15j), (1.76-0.18j), (1.77-0.21j), (1.79-0.25j), (1.81-0.3j), (1.83-0.36j)])

circuit = CustomCircuit(initial_guess=[1.45, .27, 2.59e-03, 7.76e-01, 9.50e-02, 8.09e-01], circuit='R_0-p(R_1,CPE_1)-CPE_2')
freq, z = preprocessing.cropFrequencies(freq, z)
freq, z = preprocessing.ignoreBelowX(freq, z)

abs_z = np.abs(z)
sigma = np.hstack([abs_z, abs_z])

# Try both, same result
circuit.fit(freq, z, weight_by_modulus=True, global_opt=False)
circuit.fit(freq, z, sigma=sigma)

z_fit = circuit.predict(freq)

fig, ax = plt.subplots(figsize=(10,10))
ax = circuit.plot(ax, f_data=freq, Z_data=z, kind='nyquist')

print(circuit)

eis_ques2

kevinsmia1939 commented 1 year ago

Hi, I found that using circuit.fit(freq, z, sigma=sigma*10000) gets a little bit more of a better fit, but not by much.

Edit: I also found that if I give more weight to certain freq, it can do similar things.

abs_z = np.abs(z)
bias = np.linspace(0.1,1,37)
abs_z = np.flip(abs_z)*bias
sigma = np.hstack([abs_z, abs_z])
circuit.fit(freq, z, sigma=sigma)
BGerwe commented 1 year ago

Hi,

That's unfortunate the fitting didn't improve more. Unfortunately, that's just how impedance fitting is sometimes, but it might suggest that the model isn't appropriate for the system. Alternatively, if you really want the first data point to be fit, you can drastically increase the weight for the first point.

Ex: Weight is 100 for first data point and 1 for all others

sigma = [100, 1, 1, . . . , 100 , 1 , 1, . . . ]