Code Monkey home page Code Monkey logo

impedance.py's People

Contributors

allcontributors[bot] avatar aloriba avatar aokomorowski avatar bgerwe avatar dacb avatar etrevis avatar hkennyv avatar jbonezzi22 avatar kevinsmia1939 avatar lktsui avatar markbouman avatar mdmurbach avatar ml-evs avatar nealde avatar nickbrady avatar nobkat avatar oslopanda avatar pangq2 avatar petermattia avatar pililac avatar rgasper avatar rowin avatar saftmacki avatar stephendkang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

impedance.py's Issues

Enable multi-character circuit elements

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:

  1. writing a new element function
  2. updating a single list, which acts as the master list of permissible elements

and we should support multi-character elements, without integers in the names.

Describe the solution you'd like
A solution will look like this:

  • A partial refactor of the evaluation framework which may or may not rely on different logic to parse the circuit string in order to support multi-character elements
  • An updated inheritance structure such that only circuit_elements.py needs to be modified in order to add / remove a viable element.

Weighted fitting based on |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.

Importing impedance.circuits[BUG]

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:

  1. Code which generates the error '...'
    import impedance.circuits
  2. Full error info
    no display found. Using non-interactive Agg backend

I was just trying to try the fitting_example.ipynb but ran into this error and discovered it was occuring with the .circuits import

Add residuals plotting for linKK outputs

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:
1-s2 0-S0013468614001005-gr3

[Element] support for modified inductance element

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?

Fix parameter in fitting

Hello,

Is there a way to fix a parameter (e.g. a resistor value) when fitting an equivalent circuit?

Thank you!

Errors in Example Notebooks

Hi impedance.py team,

I found some errors while using some of your example notebooks. See below:

  1. In the notebook, fitting_example.ipynb, part 4a, numpy is called without an import. Import line needs to be added in this block or earlier.
  2. In the notebook plotting_example.ipynb, part 3a, I am getting an error. There could be a bug in the function circuit.plot. (error attached as photo)

Screen Shot 2020-07-17 at 2 51 01 PM

Let me know if I can clarify anything!

Improve test coverage

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?

Consistent naming scheme for circuit elements

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:

  • A (Semi-infinite Warburg) -> W
  • W (Blocked finite-length Warburg i.e. the one with the coth) -> Wb or Wo (looks like it's typically called open)
  • [should add] (Transmissive finite-length Warburg, i.e. the one with the tanh) -> Wt or Ws (looks like it's typically called short)
  • T (Macrohomogeneous porous electrode transmission line model) -> PE or TLMp?

[BUG] Gamry data files fail to load if experiment was aborted

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])))
85

ValueError: could not convert string to float: 'T'

To Reproduce
Steps to reproduce the behavior:

  1. Attempt to load the attached file using the preprocessing.readGamry function.

Expected behavior
Attempting to load the attached file should load the data that was obtained before experiment was aborted.

exampleDataGamryABORT.DTA.txt

Rename to exampleDataGamryABORT.DTA since Github doesn't support attaching .DTA files.

[BUG] Hard-Coded Columns for readBioLogic

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.

More data formats supported

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.

Add Bode Plots

This package features a standard way of producing Nyquist plots so an analogous method should be created for Bode plots.

example notebook fitting_example.ipynb calls outdated version of impedance.plotting.plot_nyquist func

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.

Units of Capacitance?

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

Plotting needs better defaults for big numbers

Not sure what the best way to address this is, but the default plotting commands don't work well on graphs which have impedances that are large. My typical values are on the order of kOhm to MOhms and the default unit mOhms resulting in gigantic numbers on the axis label.

out_default

[DOC] Sphinx config is out of date

the sphinx documentation builds the site title incorrectly showing version 0.5.1. this shows up in the google search as well, which could be misleading

case1

image

case2

image

attaching PR

[Documentation] Gerischer element documentation unclear

The documentation for the Gerischer element is:
image

But in circuit_elements.py the definition of this element is:
image
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) :
image
With equivalence between parameters as:
image

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.

Is it possible to fix a value in the fit?

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!

Better checking/handling of custom circuit string definitions

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.

readZPlot fails to read some .z files[BUG]

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"

Error: "impedance/tests/test_circuit_elements.py:41:24: E999 Syntax Error: invalid syntax"

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.

[DATA] Biologic MPR files

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

"no display found. Using non-interactive Agg backend" in windows

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:

  1. Code which generates the error
import matplotlib.pyplot as plt
from impedance.circuits import Randles

plt.plot([1, 2, 3, 4])
plt.show()
....
  1. Full error info
    I am getting the following message when I am importing 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

  • how I installed
    pip install impedance==0.3.0
  • operating system
    windows 10

Verbose print mode

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.

[Ls - de Levie Pore _Finite Length Element]

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:

  1. 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.

  2. r De Levie, Adv. electrochem. Elecrochem. eng., 6(1967) 329.

[BUG] Custom circuit fails if circuit string starts and ends with a parallelized elements

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

if circuit.endswith(')') and circuit.startswith('p('):
circuit = circuit[2:-1]

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.

expand the docs, especiallu adding examples,

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

[BUG] Installing impedance.py from source doesn't work

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.

Handling nested parallel groups

Currently the parsing of the circuit string means can't handle a nested p(x,y) groups.

Failing example:

circuit = 'p(p(R_1, C_1)-p(R_2, C_2), C_3)'

image

Ideas: Use regular expressions, (p\(.+\)) for example, to parse out groupings.

TypeError when initial_guess is None

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

if initial_guess is not None:

I see two ways to solve this:

  • raise an exception when initial_guess is None
  • edit the doc to specify that initial_guess mandatory (I didn't see it, but if this information is already in the doc, sorry)

Thanks,

[BUG]

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)

Add more plotting options to plot_nyquist

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)

[BUG] Prediction fails on circuits of one element

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.

Paper gameplan

πŸŽ‰ 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.

Authorship πŸ“š

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

Remaining Todos 🏷

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?

Finding an error

ModuleNotFoundError: No module named 'impedance'

could please help me in removing this error

[DATA] Support for Biologic .mpr files

  • What instrument/software/file type are you requesting support for? *

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)]

validation.linKK manual mode doesn't work[BUG]

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)

[Element] Add finite-length Gerischer element

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:
Z= \frac {R_{chem}} {\sqrt{1+j2 \pi f t_G}} tanh(\phi \sqrt{1+j2 \pi f t_G} )

This element should be named Gs in line with the naming scheme discussed in #83

Fitting method not passed to scipy.optimize.curve_fit in circuit.fit method[BUG]

The circuit_fit function in fitting.py lists accepts a method argument but never passes it to curve_fit function call.

To show this behavior, I'll work off the example in the Getting Started document.

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]

By specifying 'lm' method and providing bounds, we should get an error, but we don't:

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: True

Initial 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]

Repeat using scipy directly to see expected behavior:

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')

Outputs:


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.")
703

ValueError: Method 'lm' only works for unconstrained problems. Use 'trf' or 'dogbox' instead.

Whereas specifying 'trf' method replicates results from .fit method

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])

linKK not working as intended[BUG]

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:
IS1a_impedance
To Figure 2 in the paper:
Schonleber Fig2

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()

ReplicateFig3a_Stock_linKK
ReplicateFig3bc_Stock_linKK

Compared to their result:
Schonleber Fig3

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).

[BUG] stale documentation for getting started

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

  • old imports in getting-started.rst
  • getting-started.ipynb is stale
  • old circuit elements in circuit string in getting-started.rst

[BUG] Defining 'Ws' or 'Gs' in custom circuits fails

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']

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.