Electrochemical impedance spectroscopy library written in Rust.
Inspired by LEVM and other impedance spectroscopy fitting libraries. This crate provides a way to run complex nonlinear least squares (CNLS) regression on frequency dependent electrical impedance data with the goal of applying various circuit models to experimental data.
The following definitions can be found in eispec::newtypes
- Representation of electrical current, voltage, impedance and power which are all wrappers around
num::complex::Complex
- Scalar quantities of frequency and angular frequency
Found in eispec::components
and eispec::circuits
.
These are general purpose building blocks for formulating circuit models.
The current available models are:
- Resistor, Capacitor, Inductor
- Constant Phase Element
- Various dielectric models (Debye, Cole-Cole, Cole-Davidson, Havriliak-Negami)
- Warburg Elements
- Gerischer Element
These can be combined in any way using series and parallel circuits
(eispec::circuits::SeriesCircuit
and eispec::circuits::ParallelCircuit
).
Users can also create their own components by implementing the eispec::components::Component<T>
trait.
This simply requires the definition of the complex impedance as a function of frequency.
CNLS is performed using the Levenberg–Marquardt algorithm applied to the real and imaginary parts of the impedance similar to the approach taken by LEVM. The code uses a modified version of rmpfit which is itself a direct Rust implementation of the CMPFIT code.
The user can run a fit by simply implementing the eispec::fit::ImpedanceModel<T>
trait on their
data structure and defining their circuit model.
The data to be fit should in a new type that wraps eispec::data::ImpedanceData<T>
.
Required data access methods can be applied using the #[impl_impedance_data_accessors]
macro.
Model parameters are defined using eispec::fit::ModelParameter
which stores:
- The initial parameter guess
- Whether the parameter is fixed or free in the fitting algorithm
- Any upper or lower limits on the parameter values
For example:
#[impl_impedance_data_accessors]
struct ImpedanceDataF64Wrapper(ImpedanceData<f64>);
impl ImpedanceModel<f64> for ImpedanceDataF64Wrapper {
fn model(&self, params: &[f64]) -> Box<(dyn Component<f64>)> {
\\ circuit model goes here
}
let mut data = ImpedanceDataF64Wrapper::from_csv("impedance-data-file.csv").unwrap();
data.set_parameters(
\\ define circuit model parameters and constraints
);
let result = data.fit().unwrap();
Example fits can be found in the examples
directory and can be run with e.g.:
cargo run --example simple_example