Skip to content

Commit

Permalink
Merge pull request #119 from ECSHackWeek/docs/faq
Browse files Browse the repository at this point in the history
Create a frequently asked questions section in the docs
  • Loading branch information
mdmurbach authored Sep 9, 2020
2 parents a8b18a2 + 2623416 commit 058734a
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 14 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,27 @@ Several example notebooks are provided in the `docs/source/examples/` directory.

Several examples can be found in the `docs/source/examples/` directory (the [Fitting impedance spectra notebook](https://impedancepy.readthedocs.io/en/latest/examples/fitting_example.html) is a great place to start) and the documentation can be found at [impedancepy.readthedocs.io](https://impedancepy.readthedocs.io/en/latest/).

## Citing impedance.py

[![DOI](https://joss.theoj.org/papers/10.21105/joss.02349/status.svg)](https://doi.org/10.21105/joss.02349)

If you use impedance.py in published work, please consider citing https://joss.theoj.org/papers/10.21105/joss.02349 as

```bash
@article{Murbach2020,
doi = {10.21105/joss.02349},
url = {https://doi.org/10.21105/joss.02349},
year = {2020},
publisher = {The Open Journal},
volume = {5},
number = {52},
pages = {2349},
author = {Matthew D. Murbach and Brian Gerwe and Neal Dawson-Elli and Lok-kun Tsui},
title = {impedance.py: A Python package for electrochemical impedance analysis},
journal = {Journal of Open Source Software}
}
```

## Contributors ✨

This project started at the [2018 Electrochemical Society (ECS) Hack Week in Seattle](https://www.electrochem.org/233/hack-week) and has benefited from a community of users and contributors since. Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
Expand Down
80 changes: 80 additions & 0 deletions docs/source/faq.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
Frequently Asked Questions
==========================

What method does impedance.py use for fitting equivalent circuit models?
------------------------------------------------------------------------
Fitting is performed by non-linear least squares regression of
the circuit model to impedance data via
`curve_fit <https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html>`_
from the `scipy.optimize` package.[1]
Real and imaginary components are fit simultaneously with uniform
weighting, i.e. the objective function to minimize is,

.. math::
\chi^2 = \sum_{n=0}^{N} [Z^\prime_{data}(\omega_n) - Z^\prime_{model}(\omega_n)]^2 +
[Z^{\prime\prime}_{data}(\omega_n) - Z^{\prime\prime}_{model}(\omega_n)]^2
where N is the number of frequencies and :math:`Z^\prime` and
:math:`Z^{\prime\prime}` are the real and imaginary components of
the impedance, respectively.
The default optimization method is the
Levenberg-Marquardt algorithm (:code:`method='lm'`) for unconstrained
problems and the Trust Region Reflective algorithm
(:code:`method='trf'`) if bounds are provided. See `the SciPy documentation
<https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html>`_
for more details and options.

[1] Virtanen, P., Gommers, R., Oliphant, T.E. et al.
SciPy 1.0: fundamental algorithms for scientific computing in Python.
Nat Methods 17, 261–272 (2020). `doi: 10.1038/s41592-019-0686-2 <https://doi.org/10.1038/s41592-019-0686-2>`_

How do I cite impedance.py?
---------------------------

.. image:: https://joss.theoj.org/papers/10.21105/joss.02349/status.svg
:target: https://doi.org/10.21105/joss.02349

If you use impedance.py in published work, please consider citing https://joss.theoj.org/papers/10.21105/joss.02349 as

.. code:: text
@article{Murbach2020,
doi = {10.21105/joss.02349},
url = {https://doi.org/10.21105/joss.02349},
year = {2020},
publisher = {The Open Journal},
volume = {5},
number = {52},
pages = {2349},
author = {Matthew D. Murbach and Brian Gerwe and Neal Dawson-Elli and Lok-kun Tsui},
title = {impedance.py: A Python package for electrochemical impedance analysis},
journal = {Journal of Open Source Software}
}
How can I contribute to impedance.py?
-------------------------------------

First off, thank you for your interest in contributing to the
open-source electrochemical community! We're excited to welcome all
contributions including suggestions for new features, bug reports/fixes,
examples/documentation, and additional impedance analysis functionality.

Feature requests and bug reports
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you want to make a suggestion for a new feature, please `make an
issue <https://github.com/ECSHackWeek/impedance.py/issues/new/choose>`_
including as much detail as possible. If you're requesting a
new circuit element or data file type, there are special issue templates
that you can select and use.

Contributing code
~~~~~~~~~~~~~~~~~

The prefered method for contributing code to impedance.py is to fork
the repository on GitHub and submit a "pull request" (PR).
More detailed information on how to get started developing impedance.py
can be found in
`CONTRBUTING.md <https://github.com/ECSHackWeek/impedance.py/blob/master/CONTRIBUTING.md>`_.

Feel free to reach out via GitHub issues with any questions!
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ The documentation can be found at
circuits
circuit-elements
fitting
faq

Indices and tables
==================
Expand Down
42 changes: 28 additions & 14 deletions impedance/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,25 +147,36 @@ def fit_linKK(f, ts, M, Z, fit_type='real', add_cap=False):
Returns
-------
elements: np.ndarray
values of fit R_k in RC elements and series R_0, L, and optionally C.
values of fit :math:`R_k` in RC elements and series :math:`R_0`,
L, and optionally C.
mu: np.float
under- or over-fitting measure
Notes
-----
Solving this as a system of equations that's linear wrt Rk, where Ax~=b
and we are trying to minimize r = ||Ax - b||**2. Fitting to the real or
imaginary parts, b = Re[Z] or Im[Z], respectively, and Ax is our model
fit, Z_hat. Z_hat = R_o + sum[k:1, M](R_k / (1 + j * w * tau_k). x is an
(M+1) x 1 matrix where the first row contains R_o and subsequent rows
contain R_k values. A is an N x (M+1) matrix, where N is the number of
data points, of independent variable values. Ex, fitting the real part of
data, the first column contains values of 1 / |Z|, the second column
contains Re[1 / (1 + j * w * tau_1)], the third contains
Re[1 / (1 + j * w *tau_k)] and so on. Fitting the complex values is
different since r = ||A'x - b'||**2 + ||A''x - b'||**2 according to Eq 14
of Schonleber [1], so finding the coefficients is done "manually" with
matrix manipulations instead of using numpy.linalg.pinv(A)
Since we have a system of equations, :math:`Ax ~= b`, that's linear wrt
:math:`R_k`, we can fit the model by calculating the pseudo-inverse of A.
:math:`Ax` is our model fit, :math:`\\hat{Z}`, and :math:`b` is the
normalized real or imaginary component of the impedance data,
:math:`Re(Z)/|Z|` or :math:`Im(Z)/|Z|`, respectively.
:math:`\\hat{Z} = R_0 + \\sum^M_{k=1}(R_k / |Z|(1 + j * w * \\tau_k))`.
:math:`x` is an (M+1) :math:`\\times` 1 matrix where the first row
contains :math:`R_0` and subsequent rows contain :math:`R_k` values.
A is an N :math:`\\times` (M+1) matrix, where N is the number of data
points, and M is the number of RC elements.
Examples
--------
Fitting the real part of data, the first column of A contains
values of :math:`\\frac{1}{|Z|}`, the second column contains
:math:`Re(1 / |Z| (1 + j * w * \\tau_1))`, the third contains
:math:`Re(1 / |Z| (1 + j * w * \\tau_2))` and so on. The :math:`R_k` values
within the x matrix are found using :code:`numpy.linalg.pinv` when
fit_type = 'real' or 'imag'. When fit_type = 'complex' the coefficients are
found "manually" using :math:`r = ||A'x - b'||^2 + ||A''x - b'||^2`
according to Eq 14 of Schonleber [1].
[1] Schönleber, M. et al. A Method for Improving the Robustness of
linear Kramers-Kronig Validity Tests. Electrochimica Acta 131, 20–27 (2014)
Expand Down Expand Up @@ -234,6 +245,9 @@ def fit_linKK(f, ts, M, Z, fit_type='real', add_cap=False):
# of Boukamp et al.
elements[0] = np.sum(ws * (Z.real - z_re.real)) / np.sum(ws)
elif fit_type == 'complex':
# x = (A*•A)^-1
# y = A*•b
# Pseudoinsverse, A^+ = (A*•A)^-1•A* and R_k values = A^+•b
x = np.linalg.inv(a_re.T.dot(a_re) + a_im.T.dot(a_im))
y = a_re.T.dot(Z.real / np.abs(Z)) + a_im.T.dot(Z.imag / np.abs(Z))
elements = x.dot(y)
Expand Down

0 comments on commit 058734a

Please sign in to comment.