ecshackweek / impedance.py Goto Github PK
View Code? Open in Web Editor NEWA Python package for working with electrochemical impedance data
License: MIT License
A Python package for working with electrochemical impedance data
License: MIT License
Describe the bug
Documentation is stale for getting-started.rst
and getting-started.ipynb
, specifically where it still uses impedance.circuits
and impedance.plotting
. Added a list to the bottom of things I caught, feel free to add
edit: There are also some issues with using outdated circuit elements.
To Reproduce
See getting-started.rst
Expected behavior
Use the new API:
from impedance.models.circuits import CustomCircuit
and
from impedance.visualization import plot_nyquist
Additional context
i've started a PR to fix this, need to update the notebook still
getting-started.rst
getting-started.ipynb
is stalegetting-started.rst
Describe the bug
Gamry adds some extra data at the end of the data file if the experiment is aborted and attempting to load it fails.
81 for line in raw_data:
82 each = line.split()
---> 83 f.append(float(each[2]))
84 Z.append(complex(float(each[3]), float(each[4])))
85ValueError: could not convert string to float: 'T'
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Attempting to load the attached file should load the data that was obtained before experiment was aborted.
Rename to exampleDataGamryABORT.DTA since Github doesn't support attaching .DTA files.
๐ The impedance.py project is now over a year and a half old ๐ (first commit was in May 2018) and it's turned into quite a useful tool (I think I've had someone come up to me saying they've found it to be useful or requesting a feature at pretty much every conference/meeting I've been at recently!). In order to further promote impedance.py and give folks some way of documenting their use of the software, I'd like to propose we write and submit a paper. The combination of low-overhead and open-focus of the Journal of Open Source Software (JOSS) seem to make sense to me (although other suggestions would be welcome). The submission essentially requires a short paper and some well-documented/organized/useful code.
I think anyone who has contributed significantly to impedance.py should be an author. Here is my current proposal, from the contributions GitHub page (but feel free to reach out on GitHub/email/slack if you have other thoughts):
Matt Murbach -- @mdmurbach
Brian Gerwe -- @BGerwe
Neal Dawson-Elli-- @nealde
Lok-kun Tsui -- @lktsui
I've created a project (https://github.com/ECSHackWeek/impedance.py/projects/1) for tracking what we need to take care of before submitting the paper. Feel free to add anything to the list or tackle some of the remaining issues. Big ones in my mind are the current refactor, the linKK issues @BGerwe has found, improved docs/error checking/handling, ...
@dacb @dt-schwartz any experience with JOSS/advice for anything else we should be thinking about when submitting a paper?
Hi impedance.py team,
I found some errors while using some of your example notebooks. See below:
Let me know if I can clarify anything!
Describe the bug
If the circuit only has one element (a single resistor or capacitor) in it, the prediction fails with a UnboundLocalError
To Reproduce
from impedance.models.circuits import CustomCircuit
import numpy as np
test_circuit = CustomCircuit(circuit='C_1', initial_guess = [1.0,])
print(test_circuit)
frequency_range = np.linspace(0,100)
test_circuit.predict(frequency_range)
Circuit string: C_1
Fit: False
Initial guesses:
C_1 = 1.00e+00 [F]
Simulating circuit based on initial parameters
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-9-0336b0f9ba3e> in <module>
4 print(test_circuit)
5 frequency_range = np.linspace(0,100)
----> 6 test_circuit.predict(frequency_range)
~/Documents/Programs/impedance.py/impedance/models/circuits/circuits.py in predict(self, frequencies, use_initial)
156 *self.initial_guess,
157 constants=self.constants, eval_string='',
--> 158 index=0)[0],
159 circuit_elements)
160
~/Documents/Programs/impedance.py/impedance/models/circuits/fitting.py in buildCircuit(circuit, frequencies, constants, eval_string, index, *parameters)
204 split = parallel
205
--> 206 for i, elem in enumerate(split):
207 if ',' in elem or '-' in elem:
208 eval_string, index = buildCircuit(elem, frequencies,
UnboundLocalError: local variable 'split' referenced before assignment
Expected behavior
It should predict the impedance response of a single resistor / capacitor.
Additional context
I was sanity checking a circuit model by testing a single resistor and it failed with this error.
Hello,
The below snippet checks if initial_guess
is not None
but there is no action taken when it is actually None
. Hence, when there is a further reference to the variable (e.g. assert len(self.initial_guess)
on #L336) the script fails with a TypeError
impedance.py/impedance/circuits.py
Line 21 in 3d62c11
I see two ways to solve this:
initial_guess
is None
initial_guess
mandatory (I didn't see it, but if this information is already in the doc, sorry)Thanks,
Now that we can have multi-letter circuit elements (#82), the options for how to name elements are endless... ๐ In reality, we should try to stick to a standard naming scheme to make it easier.
Currently, the elements that I think should be renamed (along with some early brainstorming...) are:
Biologic's new data file ".mpr". The exact headers are difficult to decipher at this moment. But galvani has reverse engineered the file to extract the data.
github.com/echemdata/galvani/blob/master/galvani/BioLogic.py
This needs to be implemented so that new data files can also be used.
[Please attach a sample file by dragging and dropping an example file (this file will be public)]
Describe the bug
When importing .circuits no display is found and it starts using the Agg backend. Thus, you can not show graphs with matplotlib
To Reproduce
Steps to reproduce the behavior:
I was just trying to try the fitting_example.ipynb but ran into this error and discovered it was occuring with the .circuits import
In some cases ZPlot doesn't write metadata about the experiment. If that happens, the file doesn't contain the "End Comments" line that readZPlot looks for to indicate the beginning of data.
To Reproduce
Download this this file.
from impedance import preprocessing
# Set file path to location of attached file
file_path = r'C:\Users\shadd\Downloads\e_0.txt'
f, Z = preprocessing.readZPlot(file_path)
Produces:
~\Anaconda3\lib\site-packages\impedance\preprocessing.py in readZPlot(filename)
274 start_line = i
275
--> 276 raw_data = lines[start_line+1:]
277 f, Z = [], []
278 for line in raw_data:
UnboundLocalError: local variable 'start_line' referenced before assignment
This can be fixed by adding some condition of finding the "End Comments" line and finding the ZPlot data headers: " Freq(Hz) Ampl Bias Time(Sec) Z'(a) Z''(b) GD Err Range"
Describe the bug
First of all, great work with the impedance package. When I import Randles
from impedance.circuits
, it does not let me plot figures using matplotlib. The package is checking environment variable DISPLAY
in curcuits.py. I think windows (I am a window user) does not have a DISPLAY
environment variable, atleast mine does not. Thats why in circuits.py, line mpl.use('Agg')
is executed. There are some potential solutions to check for display in windows in this post
To Reproduce
Steps to reproduce the behavior:
import matplotlib.pyplot as plt
from impedance.circuits import Randles
plt.plot([1, 2, 3, 4])
plt.show()
....
Randles
from impedance.circuits
.no display found. Using non-interactive Agg backend
C:\Users\Sumbal\Documents\atom\data-analysis\antranik\controller\controller.py:193: UserWarning: Matplotlib is currently using agg, which is a non-GUI backend, so cannot show the figure.
plt.show()
Expected behavior
This message above should not appear.
Additional context
pip install impedance==0.3.0
windows 10
Data validation is an important step if we want to make following best practice easy for folks.
A few methods I can think of:
We've started playing around with measurement models, but it's in a very early stage...
Our latest coverage report shows:
Name Stmts Miss Cover
-----------------------------------------------------------
impedance\__init__.py 0 0 100%
impedance\models\__init__.py 0 0 100%
impedance\models\circuits\__init__.py 1 0 100%
impedance\models\circuits\circuits.py 183 15 92%
impedance\models\circuits\elements.py 81 1 99%
impedance\models\circuits\fitting.py 126 3 98%
impedance\preprocessing.py 154 21 86%
impedance\validation.py 58 30 48%
impedance\visualization.py 89 0 100%
-----------------------------------------------------------
TOTAL 692 70 90%
----------------------------------------------------------------------
I'll be tackling coverage in validation
before I close out #69. Tt would be great to capture the loose ends in circuits
, elements
and fitting
, and improve preprocessing
. Can we make it to 100% before v1.0?
Is your feature request related to a problem? Please describe.
When I guess a value, I need to give a range. If the range is too small, the fit takes a long time. There are certain values I want to fix in the fit, but can't see an option to do so.
Describe the solution you'd like
By providing a fixed value, the fit should take less time (I think??)
Describe alternatives you've considered
A small boundary works, but is still computationally intensive.
Is there currently a way to do this that I can't find? Thanks!
Describe the bug
Trying to define a custom circuit with 'Ws' or 'Gs' raises an error that the number of initial guesses doesn't match the circuit length. This seems to come from extract_circuit_elements
incorrectly removing the s
at the end of each element name.
To Reproduce
import sys
sys.path.append('../../../')
from impedance.models.circuits import CustomCircuit
circ = 'R_0-Gs_1-Ws_1'
inits =[10, 100, .1, .3, 50, 1]
sim = CustomCircuit(circuit=circ, initial_guess=inits)
>>AssertionError: The number of initial guesses + the number of constants (6) needs to be equal to the circuit length (4)
Expected behavior
The circuit should be successfully created with 6 initial guess parameters. The number of parameters for each element are: R_0 = 1, Gs_1 = 3, Ws_1 = 2.
Additional context
Running the following shows that Gs
and Ws
are incorrectly identified as G
and W
from impedance.models.circuits.fitting import extract_circuit_elements
extract_circuit_elements(circ)
>>['R_0', 'G_1', 'W_1']
Hello,
Is there a way to fix a parameter (e.g. a resistor value) when fitting an equivalent circuit?
Thank you!
Printing circuit model data should include units
Hello,
Came across this minor documentation bug when running the fitting_example.ipynb
notebook.
this block of code passes an outdated argument to the plot_nyquist
function.
plot_nyquist(ax, f_pred, randles_fit, fmt='-')
plot_nyquist(ax, f_pred, randlesCPE_fit, fmt='-')
plot_nyquist(ax, f_pred, customCircuit_fit, fmt='-')
It should be corrected to this to achieve the same behavior:
plot_nyquist(ax, f_pred, randles_fit, fit=True)
plot_nyquist(ax, f_pred, randlesCPE_fit, fit=True)
plot_nyquist(ax, f_pred, customCircuit_fit, fit=True)
I'm a relatively new contributor, but I'd gladly submit a PR if you could tell me which branch to submit it to. I noticed the master branch's source code still uses the old fmt
parameter, but the package on PyPi uses the new parameter.
I tried replicating results from the Schonleber et al. paper this module is based on, but found some key differences to their results. The focus, for now, will be on Figure 3 from the paper.
To Reproduce
First, I simulated a circuit for their IS1 data using parameters listed in Table 1 of the paper.
import numpy as np
import matplotlib.pyplot as plt
from impedance.circuits import CustomCircuit
from impedance.plotting import plot_nyquist
from impedance.validation import linKK
circ = 'R1-p(R2,C3)-p(R4,E5)'
vals = [100, 200, 0.8e-6, 500, 0.4e-3, 0.5]
f = np.logspace(7,-6, num=131)
IS1 = CustomCircuit(circ, initial_guess=vals)
Z_IS1 = IS1.predict(f)
fig, ax = plt.subplots()
plot_nyquist(ax, f, Z_IS1)
ax.set_ylim(0, 400)
ax.set_xlim(0, 900)
plt.show()
Compare this result:
To Figure 2 in the paper:
It looks pretty close, though a quantitative comparison is difficult without their data.
Now, add noise to IS1 to get IS1a:
sigma_a = 1000
Z_IS1a = (Z_IS1.real + np.abs(Z_IS1)/sigma_a) + 1j *(Z_IS1.imag + np.abs(Z_IS1)/sigma_a)
And use manual mode of linKK to fit with M=6 ("Underfitting") and M=104 ("Overfitting")
IS1a_uf = linKK(f, Z_IS1a, c=None, max_M=6)
IS1a_of = linKK(f, Z_IS1a, c=None, max_M=104)
fig, ax =plt.subplots()
plot_nyquist(ax, f, Z_IS1a, fmt='.')
plot_nyquist(ax, f, IS1a_uf[2], fmt='-')
plot_nyquist(ax, f, IS1a_of[2], fmt='--')
ax.set_ylim(0, 400)
ax.set_xlim(0, 900)
ax.legend(labels=('IS1a', 'Underfitting', 'Overfitting'))
plt.show()
fig, (ax0, ax1) =plt.subplots(nrows=2, figsize=(6,6))
ax0.plot(np.log10(f), IS1a_uf[3], '-', label=r'$\Delta_{Re}$')
ax0.plot(np.log10(f), IS1a_uf[4], '--', label=r'$\Delta_{Im}$')
ax1.plot(np.log10(f), IS1a_of[3], '-', label=r'$\Delta_{Re}$')
ax1.plot(np.log10(f), IS1a_of[4], '--', label=r'$\Delta_{Im}$')
ax0.set_ylim(-.12, .12)
ax1.set_ylim(-.12, .12)
ax0.set_xlim(-6, 6)
ax1.set_xlim(-6, 6)
ax1.set_xlabel('log(f) / Hz')
ax0.locator_params(axis='y', nbins=3, tight=True)
ax1.locator_params(axis='y', nbins=3, tight=True)
ax0.locator_params(axis='x', nbins=3, tight=True)
ax1.locator_params(axis='x', nbins=3, tight=True)
ax0.legend()
ax1.legend()
ax0.grid(True)
ax1.grid(True)
plt.show()
Notably, the first visible arc in the "underfitting" case from impedance.py linKK is much larger than their figure. Further, the residuals lines have the same general shape but don't quantitatively agree โ most easily seen by comparing the residuals lines at log(f) = 0.
I believe this behavior arises from the way impedanc.py linKK calculates the distribution of time constants using linear frequencies (vs radial frequencies as used in the paper).
Have had a few requests for the ability to export a circuit to a pdf/svg/png
After some poking around I think SchemDraw (https://cdelker.bitbucket.io/SchemDraw/SchemDraw.html) seems like a good way to do it
This package features a standard way of producing Nyquist plots so an analogous method should be created for Bode plots.
This element describes the impedance of a pore with conductive and reactive pore walls. This is in contrast to Finite Length Warburg element where the pores are non-reactive (infinite polarization resistance).
Z(w) = (R/(gamma)^1/2) * coth((gamma)^1/2)
where Z is the impedance , R is the resistance, w is the angular frequency and
gamma = (1/A)+B*(jw)^phi
The following referenes give a detailed description of the electrochemical model:
A.Lasia, "Impedance of porous electrodes", Modern Aspects of electrochemistry, "Modellingand Numerical Simulations," vol.43, p.67-138, M.Schlesinger, Ed.,Springer, 2009, IsBN: 978-0-387-49580-4.
r De Levie, Adv. electrochem. Elecrochem. eng., 6(1967) 329.
Describe the bug
Cloning the repo and installing from source raises UnicodeDecodeError: 'charmap' codec can't decode byte 0x8f in position 4236: character maps to <undefined>
To Reproduce
Clone repo:
$ git clone https://github.com/ECSHackWeek/impedance.py.git
remote: Enumerating objects: 93, done.
remote: Counting objects: 100% (93/93), done.
remote: Compressing objects: 100% (78/78), done.
remote: Total 1944 (delta 39), reused 55 (delta 14), pack-reused 1851
Receiving objects: 100% (1944/1944), 5.20 MiB | 10.64 MiB/s, done.
Resolving deltas: 100% (1210/1210), done.
Switch to that directory:
cd impedance.py/
Try to install:
python setup.py install --user
Expected behavior
It should successfully install the package
running install
...
Using c:\users\shadd\anaconda3\lib\site-packages
Finished processing dependencies for impedance==0.5.1
Additional context
As suggested in this thread, changing with open("README.md", "r") as fh:
to with open("README.md", "r", encoding='utf8') as fh:
seems to fix the problem.
In "exampleDataBioLogic.mpt" it appears that frequency, Im(Z), and Re(Z) make up the 1st, 2nd and 3rd columns respectively, but I haven't found this to be universal.
This is an issue because in preprocessing.py, function readBioLogic (as well as other funcs), the column numbers are hard coded as 1, 2, 3.
I have an .mpt file (couldn't share here - but willing to share) where these columns, are in a different order, but the headers remain the same: "freq/Hz", "Re(Z)/Ohm", "-Im(Z)/Ohm".
If I use pandas to import from the mpt file it works very easily. I noticed you are not using pandas but perhaps there is a way to resolve this.
Proposed solution: (line 151 of preprocessing.py)
# find the freq and Z columns
headers = lines[number_header_lines-1].split('\t')
freq_col = [o for o, h in enumerate(headers) if h == 'freq/Hz'][0]
ReZ_col = [o for o, h in enumerate(headers) if h == 'Re(Z)/Ohm'][0]
ImZ_col = [o for o, h in enumerate(headers) if h == '-Im(Z)/Ohm'][0]
raw_data = lines[number_header_lines:]
f, Z = [], []
for line in raw_data:
each = line.split('\t')
f.append(float(each[freq_col]))
# MPT data format saves the imaginary portion as -Im(Z) not Im(Z)
Z.append(complex(float(each[ReZ_col]), -1*float(each[ImZ_col])))
This works on my data file as well as exampleDataBioLogic.mpt
But again, pandas does this already.
Custom circuit fails if circuit string starts and ends with a parallelized element (e.g. 'p(R1,C1)-R0-p(R2,C2)'
fails, but 'R0-p(R1,C1)-p(R2,C2)'
or 'p(R1,C1)-p(R2,C2)-R0'
parse correctly)
Full error info
~/miniconda3/envs/___/lib/python3.7/site-packages/impedance/fitting.py in buildCircuit(circuit, frequencies, eval_string, index, *parameters)
206 split = series
207
--> 208 for i, elem in enumerate(split):
209 if ',' in elem or '-' in elem:
210 eval_string, index = buildCircuit(elem, frequencies,
UnboundLocalError: local variable 'split' referenced before assignment
This has to do with the way that we parse circuit strings, particularly these line
impedance.py/impedance/fitting.py
Lines 169 to 170 in a32ae19
For now, assuming there's at least one non-parallel element in the circuit, the work around is relatively easy: just make either the first or last element non-paralllel.
This was discussed at ECS Hacking Spring on 10/3 - We would separate string generation for generic and also for verbose printing of a circuit's parameters and confidence data.
ModuleNotFoundError: No module named 'impedance'
could please help me in removing this error
Is your feature request related to a problem? Please describe.
This feature request is two-fold - adding a new circuit element should be as easy as:
and we should support multi-character elements, without integers in the names.
Describe the solution you'd like
A solution will look like this:
https://github.com/ECSHackWeek/impedance.py/blob/master/examples/fitting_tutorial.ipynb linked on the front page is a dead link.
Is your feature request related to a problem? Please describe.
In the validation module linKK returns the real and imaginary residuals, but there is no built in way to visualize them.
Describe the solution you'd like
A plotting function to produce standardized visualization of the fit residuals as seen in the Schรถnleber, M. et al. paper. Figure 3 from that paper, for example:
Is your feature request related to a problem? Please describe.
As of 2019-10-17, we currently support Autolab, Parstat, and Gamry in the preprocessing input. We would like to support more formats to make it easier to get your data into the analyzer easily.
Describe the solution you'd like
We should support the importing of data formats from other instrument manufacturers. If you have an example of data format, please submit it here and we can work on adding support for your data format to the software.
Describe alternatives you've considered
It could be possible to get your data into a generic CSV, but that may require manual editing of the data into the format that our read generic file supports and that is an additional hurdle towards adoption of this software.
Altair is an awesome visualization package built on Vega-Lite that can provide interactive graphics options for the output impedance.py plots.
Is your feature request related to a problem? Please describe.
The current plot_nyquist method doesn't accept matplotlib options that are beneficial when plotting multiple data sets. A few options that come to mind: line and marker style formatting, specified line colors, and line labels. Furthermore, the method requires freq
as a positional argument, but doesn't actually use it.
Describe the solution you'd like
Accepting kwargs in the function call and passing them to the plotting function call
>42 ax.plot(np.real(Z), -np.imag(Z), fmt, lw=3)
The error that gets thrown if an incorrect circuit is specified is not intuitive (the error is raised when the number of initial guesses doesn't match the circuit). For example,
custom = CustomCircuit('F0-T0', initial_guess=[1, 2, 3, 4, 5])
raises AssertionError: Initial guess length needs to be equal to {circuit_length}
.
We could add a check during initialization that all the elements specified are allowed and raise a more useful error at that time.
preprocessing.readGamry returns a tuple instead of an array. In the "Preprocessing" documentation, it says this should return an array of the frequencies and complex numbers, but when checking the type, it returns a tuple. Not a big issue, but the indexing is different, and can cause other problems later.
My original code:
import numpy as np
import impedance
import impedance.preprocessing
data = impedance.preprocessing.readGamry(os.path.join("filename.DTA"))
frequencies = data[:,0] <--error in this line because tuple is indexed by 1 number only (but I was indexing it as an array, with 2 numbers)
Hey! I'd like to request adding a modified inductance element to impedance.models.circuits.elements
.
Some background info on this element:
https://www.biologic.net/documents/battery-eis-modified-inductance-element-electrochemsitry-application-note-42/
The equation is similar to the CPE, except with inductance instead of capacitance (apologies, don't know how to render mathjax here):
Z = L(jw)^alpha
I have a fork with this component implemented as M
(shown below), but it looks like there's support for multi-letter elements now so maybe we can find a more suitable representation?
def M(p, f):
""" defines a modified inductor
.. math::
Z = (L \\times j 2 \\pi f)^\\alpha}
where :math:`L` = p[0] and :math:`\\alpha` = p[1].
"""
typeChecker(p, f, M.__name__, 2)
omega = 2*np.pi*np.array(f)
inductance = p[0]
alpha = p[1]
return (inductance*1j*omega)**alpha
I wouldn't mind putting in the PR myself and adding some tests. How's this sound? Is there any other demand for this?
In porous SOFC electrodes where the utilization length is longer than the thickness of the electrode, the Gerischer element isn't appropriate. A treatment for this situation was first introduced (I think) in this paper Where the impedance can be mathematically represented as:
This element should be named Gs
in line with the naming scheme discussed in #83
First, thanks for the great work and providing such a cool tool!
Second, with the potential on looking stupid, I did not figure out the units of the capacitance yet. Is it F, ยตF or what is it?
Thanks,
Matthias
The documentation for the Gerischer element is:
But in circuit_elements.py the definition of this element is:
The documentation should be updated to reflect the code.
Alternatively, this element could be defined following Lu, Kreller, and Adler, 2009 (DOI: 10.1149/1.3079337) :
With equivalence between parameters as:
The benefit of this definition is R_G is interpretable as the impedance width (rather than an admittance as is the case with Y), and t_G is a characteristic time constant where the system begins transitioning from co-limited kinetics and diffusion to only diffusionally limited.
The circuit_fit function in fitting.py lists accepts a method argument but never passes it to curve_fit function call.
import sys
sys.path.append('../../../')
from impedance.circuits import Randles, CustomCircuit
init = [.01, .005, .1, .001, 200]
randles = Randles(initial_guess=init)
import numpy as np
data = np.genfromtxt('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]
bounds = ([1e-3, 1e-3, 1e-3, 1e-3, 1e-3],[1e3, 1e3, 1e3, 1e3, 1e3])
randles.fit(frequencies, Z, bounds=bounds, method='lm')
print(randles)
Name: Randles
Circuit string: R0-p(R1,C1)-W1
Fit: TrueInitial guesses:
R0 = 1.00e-02 [Ohm]
R1 = 5.00e-03 [Ohm]
C1 = 1.00e-01 [F]
W1_0 = 1.00e-03 [Ohm]
W1_1 = 2.00e+02 [sec]Fit parameters:
R0 = 1.86e-02 (+/- 2.44e-04) [Ohm]
R1 = 1.15e-02 (+/- 3.10e-04) [Ohm]
C1 = 1.33e+00 (+/- 1.01e-01) [F]
W1_0 = 6.31e-02 (+/- 4.47e-03) [Ohm]
W1_1 = 2.22e+02 (+/- 3.56e+01) [sec]
from scipy.optimize import curve_fit
from impedance.fitting import wrapCircuit
popt, pcov = curve_fit(wrapCircuit(randles.circuit, randles.constants), frequencies, np.hstack([Z.real, Z.imag]), p0=init, bounds=bounds, maxfev=100000, ftol=1E-13, method='lm')
ValueError Traceback (most recent call last)
in
1 popt, pcov = curve_fit(wrapCircuit(randles.circuit, randles.constants), frequencies,
2 np.hstack([Z.real, Z.imag]), p0=init,
----> 3 bounds=bounds, maxfev=100000, ftol=1E-13, method='lm')~\Anaconda3\lib\site-packages\scipy\optimize\minpack.py in curve_fit(f, xdata, ydata, p0, sigma, absolute_sigma, check_finite, bounds, method, jac, **kwargs)
699
700 if method == 'lm' and bounded_problem:
--> 701 raise ValueError("Method 'lm' only works for unconstrained problems. "
702 "Use 'trf' or 'dogbox' instead.")
703ValueError: Method 'lm' only works for unconstrained problems. Use 'trf' or 'dogbox' instead.
popt, pcov = curve_fit(wrapCircuit(randles.circuit, randles.constants), frequencies, np.hstack([Z.real, Z.imag]), p0=init, bounds=bounds, maxfev=100000, ftol=1E-13, method='trf')
popt
array([1.86146618e-02, 1.15477176e-02, 1.33331944e+00, 6.31473595e-02,
2.22407301e+02])
Hi everyone,
this is probably the wrong way of making my suggestion, if so I apologize.
I am a beginner in Python, and I have just started using impedance.py instead of commercial software or software developed in my university.
I think that new users, especially those who are not great at programming, could benefit from a larger set of examples/tutorials, to reduce the number of points where they could get stuck when learning how to use the package, kind of what is possible to do with, for example, matplotlib's tutorials.
As a quick, dumb example, I am not 100% sure how to pass the initial guess parameters of circuit elements, and sometimes get stuck between two errors such as
"in E, input list must be length 2"
"Initial guess length needs to be equal to {circuit_length}"
when building a new custom circuit :)
These examples/tutorials could show the fitting of more models, maybe include an example of workflow/best practice, or highlight things that would be more laborious with software like Zview.
I understand it is a small community of users, but I would guess that many of the experienced users would already have scripts that would require minor modifications to be used as tutorials, so that it may be done without too much extra work.
Best,
Nicola
Hi @mdmurbach
I added the snippet code for the impedance of "Ls-de Levie Pore - finite element" in "impedance.py/impedance/models/circuits/elements.py /" and removed all the PEP8 errors.
Later in the "impedance.py/impedance/tests/test_circuit_elements.py /" , added key 'Ls' and it values for the said frequencies in the 'correct_vals' dictionary.
After committing , Error: "impedance/tests/test_circuit_elements.py:41:24: E999 Syntax Error: invalid syntax" has shown. Could you please look into this error and resolve it. And then merge the element in the source code.
If possible provide me with the solution for the error and the further steps in merging the element to the source code which would be helpful in the future.
Feel free to make changes in the code I have added to the files and let me know. so that I will not repeat the errors again.
Thanks in advance.
Hey @mdmurbach -- this package is awesome. Great work to you and the other contributors.
In addition to MPT files (which are manually exported from Bio-logic's ECLab), it would be awesome to be able to read in the binary MPR files saved by ECLab as well. This functionality would allow users to avoid ECLab altogether for postprocessing.
Fortunately, the galvani package has MPR reading functionality in its BioLogic.py file. The PyPI release lags the repo, so I simply downloaded BioLogic.py and placed it in the same directory.
Here is a sample file:
test.mpr.zip
I have a template function here that works, but this function could probably use more input validation.
from BioLogic import MPRfile # local version; use galvani package if it's updated
def read_mpr(filename):
"""
Function for reading the .mpr file from Biologic
EC-lab software
Parameters
----------
filename: string
Filename of .mpr file to extract impedance data from
Returns
-------
frequencies : np.ndarray
Array of frequencies
impedance : np.ndarray of complex numbers
Array of complex impedances
"""
file = MPRfile(filename) # MPRfile class located in BioLogic.py from galvani package
df = pd.DataFrame(file.data) # MPR file
# Convert to numpy
f = df['freq/Hz'].to_numpy()
Z = df['Re(Z)/Ohm'].to_numpy() - 1j*df['-Im(Z)/Ohm'].to_numpy()
# Convert from complex64 to complex
Z = Z.astype(complex)
return f, Z
Currently the fitting is based on real and img values of Z without any weighting. This approach puts a bias on low frequency data points. I believe the standard weighting scheme used in many impedance fitting software is to use the absolute impedance.
This can be easily implemented. In the circuit_fit
function where you call curve_fit
from scipy, you can do something like:
abs_Z = np.abs(Z)
sigma = np.hstack([abs_Z, abs_Z])
which will be the inverse weights. The curve_fit
in scipy has an optional sigma
argument that can be used for weighting, to which sigma
can be supplied.
This small change greatly improves the EIS fitting quality in some data sets.
Not sure whether it's better to add this as an option or to change the default fitting scheme. So I'm submitting this as a feature request.
Setting c=None
and setting a manual number of RC elements e.g. max_M=3
gives the error
fitLinKK() missing 1 required positional argument: 'Z'
Code to reproduce:
import numpy as np
# read data
data = np.genfromtxt('data/exampleData.csv', delimiter=',')
f = data[:,0]
Z = data[:,1] + 1j*data[:,2]
mask = f < 1000
f = f[mask]
Z = Z[mask]
from impedance.validation import linKK
M, mu, Z_linKK, res_real, res_imag = linKK(f, Z, c=None, max_M=3)
This should be a simple fix by changing line 94 from p_values, mu = fitLinKK(f, M, Z)
to p_values, mu = fitLinKK(f, ts, M, Z)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.