Skip to main content

eisfit

Version Python License

eisfit is a lightweight Python package for fitting electrochemical impedance spectroscopy (EIS) data and extracting equivalent-circuit parameters.

The package supports a two-arc equivalent circuit with either a CPE-type low-frequency tail or a semi-infinite Warburg tail:

Z = jωL + R0 + (R1 || CPE1) + (R2 || CPE2) + tail

It is designed for battery EIS analysis workflows where users need a simple Python API, a command-line tool, and reproducible fitted outputs.

Features

  • Load EIS data from CSV, XLS, and XLSX files
  • Fit complex impedance data with bounded multi-start least squares
  • Use either a flexible CPE tail or a Warburg diffusion tail
  • Export fitted parameters, simulated impedance, residuals, and Nyquist plots
  • Run from Python notebooks, scripts, or the command line
  • Use included demo files to test the workflow immediately after cloning

Installation

Install the released package from PyPI:

pip install eisfit

For local development from a cloned repository:

pip install -e ".[dev]"

Quick Start

Fit one EIS file directly:

from eisfit import fit_file

result = fit_file("my_eis.csv", tail="cpe", n_starts=40)

print(result.parameters)
print(result.rmse, result.nrmse)

Convergence and diagnostic records

Only optimizer starts that converge and return finite parameters, residuals, cost, fitted impedance and fit error can be selected. The lowest-cost accepted start is returned. If every start fails, fit() and fit_file() raise FitConvergenceError instead of returning an unconverged parameter set:

from dataclasses import asdict
from eisfit import FitConvergenceError, fit

try:
    result = fit(freq, Zexp)
except FitConvergenceError as error:
    attempts = [asdict(attempt) for attempt in error.attempts]
else:
    print(result.diagnostics)

Each attempted start records its index, optimizer success/status/message, evaluation count, cost, acceptance, rejection reason, start and final parameters, and bound hits (see below). A failed file fit does not write new fitted outputs. The CLI reports convergence failures as JSON and exits with status 1; a folder run continues with the remaining files and exits with status 1 if any file fails. Existing outputs from earlier runs are not removed, so consumers should use the current result's returned paths.

Convergence is a numerical termination condition. It does not establish a unique solution, adequate fit quality or a physical interpretation. identifiability_status is explicitly not_assessed.

Arc parameter ordering

Reported R1/Q1/n1 and R2/Q2/n2 are ordered by increasing characteristic time tau = (R * Q) ** (1 / n) for each parallel R-CPE branch. Arc 1 therefore has the higher characteristic frequency. Exact time ties use (R, Q, n) as a deterministic secondary ordering. The full fitted spectrum is unchanged.

This convention prevents an arbitrary exchange of the two branch labels. It does not assign SEI/charge-transfer processes, establish identifiability, or track the same physical arc across spectra, especially when the times overlap or cross.

result.parameters, p_opt, staged fit errors, parameter_bounds and bound_hits use the reported order. result.optimizer retains SciPy's original solver order, including x and Jacobian columns. To relate them:

reported = result.optimizer.x[list(result.parameter_permutation)]

The permutation and effective parameter bounds are included in result.diagnostics. Custom bounds are permuted with their branch, so bound warnings remain associated with the correct reported parameter.

Multistart audit and near-best starts

Each FitAttempt records start_parameters, the values passed to the optimizer in solver order, and parameters, its final values in the reported arc order, together with that start's parameter_permutation and bound_hits. Non-finite final values are recorded as None, so attempts stay valid JSON.

result.diagnostics["near_best"] lists the accepted starts whose cost is within FitConfig.near_best_rtol (default 0.01, i.e. 1 %) of the best cost, and the range of each parameter across them:

from eisfit import FitConfig, fit_model_eis

result = fit_model_eis(freq, Zexp, FitConfig(n_starts=40, near_best_rtol=0.01))
near = result.diagnostics["near_best"]
print(near["start_count"], near["parameter_ranges"]["R1_Ohm"])

The tolerance only defines what is reported; it does not change which start is selected. Agreement among near-best starts describes this optimizer run with these bounds and settings. It does not establish identifiability, parameter uncertainty or a physical interpretation; a wide range shows that fits of almost equal cost exist with different parameter values.

Parameter units

result.parameter_units, parameter_units(tail) and the diagnostics give the unit of every reported parameter: L_H in H, R*_Ohm in Ohm, Q1/Q2/Qd in S·s^n where n is the exponent of the same element, n1/n2/nd dimensionless, and sigma in Ohm·s^(-1/2).

Evaluating a fitted model

Evaluate the circuit from named parameters at any frequencies in Hz, for example to draw a smooth fitted curve:

import numpy as np
from eisfit import model_impedance

dense_freq = np.logspace(5, -2, 400)
Z_dense = model_impedance(result.parameters, dense_freq, tail=result.tail)

Load the data first when you want direct access to frequency and impedance arrays:

from eisfit import fit, load_eis

freq, Zexp = load_eis("my_eis.csv")
result = fit(freq, Zexp, tail="warburg")

print(result.parameters)

freq is the frequency array in Hz. Zexp is the measured complex impedance array:

Zexp = Zreal + 1j * Zimag

Input Data

Input files should contain at least three columns:

Quantity Meaning
frequency Frequency in Hz
real impedance Real part of impedance, usually Zreal or Zre
imaginary impedance Imaginary part of impedance, usually Zimag or Zim

Extra columns are allowed. eisfit tries to infer common column names automatically.

Example CSV:

Frequency_Hz,Zreal_Ohm,Zimag_Ohm
100000,0.0182,-0.0014
79432.8,0.0184,-0.0017
63100,0.0187,-0.0021

Models

Default CPE-tail model:

tail = 1 / (Qd * (jω)^nd)
parameters = [L, R0, R1, Q1, n1, R2, Q2, n2, Qd, nd]

Warburg-tail model:

tail = σ / sqrt(jω)
parameters = [L, R0, R1, Q1, n1, R2, Q2, n2, sigma]

The CPE tail keeps the diffusion-like exponent free. The Warburg tail uses the traditional semi-infinite Warburg form.

Command Line

Fit one file:

eisfit my_eis.csv --out-dir outputs --n-starts 40

Use the Warburg tail:

eisfit my_eis.csv --tail warburg --out-dir outputs --n-starts 40

Fit all matching files in a folder:

eisfit my_eis_folder --pattern "*.csv" --out-dir outputs --n-starts 20

Outputs

For each fitted file, eisfit can generate:

  • fitted equivalent-circuit parameters
  • simulated impedance values
  • residuals between measured and fitted impedance
  • Nyquist plot PNG
  • Excel output table for downstream analysis
  • optimizer-attempt audit sheet in the Excel output, and an attempt_parameters sheet with each start's start and final parameters
  • *_fit_diagnostics.json containing parameters, parameter units, solver diagnostics and the actual requested fitting configuration, with effective bounds in diagnostics

Figure options are separate from the fitting configuration:

from eisfit import FitConfig, PlotConfig, process_file

process_file("my_eis.csv", cfg=FitConfig(n_starts=40), out_dir="outputs", plot=PlotConfig(dpi=150))

Demo

The repository includes example EIS files and a demo notebook:

examples/demo_fit_zenodo_dataset.ipynb

You can run the notebook after installing the local development environment:

pip install -e ".[dev]"
jupyter notebook

Development

Run the test suite:

pytest

Run a basic import/compile check:

python -m compileall src tests

Build the package locally:

python -m build

Release

Releases are published from main by pushing a version tag; GitHub Actions and Trusted Publishing build and upload the package. See RELEASE.md for the release workflow.

License

The package code is released under the MIT License.

Metadata

Release files for eisfit 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for eisfit 0.2.0
File Size Uploaded Provenance Actions
eisfit-0.2.0.tar.gz 16.3 kB With provenance Details for eisfit-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for eisfit 0.2.0
File Interpreter ABI Platform Actions
eisfit-0.2.0-py3-none-any.whl Python 3 none any Details for eisfit-0.2.0-py3-none-any.whl

Total release size: 35.3 kB

Release files / eisfit-0.2.0.tar.gz

Download URL eisfit-0.2.0.tar.gz
Size 16.3 kB
Tags Source
SHA-256 checksum
How to use checksums
2e9eacd7bca3c72eda74d18f824ba40533619eabbfa57e34a787e9f2bac62228
BLAKE2b-256 checksum
How to use checksums
c082bdbb1d48f0b6d9407fe33a71ca7fb7e87e561a7360a8acb5edd5bea76f52
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release files / eisfit-0.2.0-py3-none-any.whl

Download URL eisfit-0.2.0-py3-none-any.whl
Size 19.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
96f5d18dd8748758932444eb76eeb0fd6a4cc2b79381d699e88501323f569d2d
BLAKE2b-256 checksum
How to use checksums
b7f9aaa54ce4a9877f659a31520d3b7134dd926e7dbc025cf53bbb8cb931d371
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Security for eisfit 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for eisfit-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page