ICA and DVA¶
Incremental capacity analysis (dQ/dV) and differential voltage analysis (dV/dQ).
from cellpy import ica
frame = ica.dqdv(c) # cycle, direction, voltage, capacity, dqdv
frame = ica.dvdq(c, direction="charge") # cycle, direction, capacity, voltage, dvdq
Both verbs accept the same three kinds of source — a CellpyCell, a curve
frame from get_cap, or a bare (voltage, capacity) pair — and the same
IcaOptions recipe.
ica ¶
Incremental capacity analysis (dQ/dV) and differential voltage analysis (dV/dQ).
Two public verbs over one pure core:
from cellpy import ica
frame = ica.dqdv(c) # cycle, direction, voltage, capacity, dqdv
frame = ica.dvdq(c, direction="charge") # cycle, direction, capacity, voltage, dvdq
Both are thin wrappers over stateless transform functions
(transform_half_cycle) configured by a
single frozen IcaOptions object: interpolate V(q),
optionally smooth, invert to q(V), differentiate, optionally smooth again,
optionally normalize.
- The output frame is specced: always long format, always the same columns,
with
directionspelled"charge"/"discharge". - The incremental-capacity column is named
dqdv. - Half-cycles that fail are reported, not silently replaced by empty arrays.
ica.Converterand the other pre-2.1 entry points are removed; usedqdv(cell, cycles=, direction=)+to_wide()(see the migration guide andDEPRECATIONS.md).
scipy stays on this side of the cellpy/cellpycore boundary: cellpycore is scipy-free, and the ICA math is interpolation and filtering, not frame algebra.
GaussianOptions
dataclass
¶
Parameters passed straight through to scipy.ndimage.gaussian_filter1d.
HalfCycleResult
dataclass
¶
HalfCycleResult(x: ndarray, y: ndarray, partner: ndarray, derivative: str, normalizing_factor: float, post_smoothing_applied: bool, notes: tuple[str, ...] = ())
Output of one half-cycle transform.
Attributes:
-
x(ndarray) –The abscissa — voltage for
dqdv, capacity fordvdq. -
y(ndarray) –The derivative itself.
-
partner(ndarray) –The other coordinate at the same points, so ICA and DVA curves can be cross-plotted. Capacity for
dqdv, voltage fordvdq. -
derivative(str) –"dqdv"or"dvdq". -
normalizing_factor(float) –The factor actually used, returned rather than mutated onto shared state, so processing one half-cycle can never leak its normalization into the next.
-
post_smoothing_applied(bool) –Whether gaussian post-smoothing survived — it is dropped when it raises (see
notes). -
notes(tuple[str, ...]) –Human-readable record of anything the transform had to work around.
IcaCols
dataclass
¶
IcaCols(cycle: str = 'cycle', direction: str = 'direction', voltage: str = 'voltage', capacity: str = 'capacity', dqdv: str = 'dqdv', dvdq: str = 'dvdq')
Column names of the specced ICA/DVA output frames.
This is a data contract: users index the returned frame by these names, so it is versioned with the package rather than left to each entry point.
ordered_names ¶
Column order for the given derivative.
IcaOptions
dataclass
¶
IcaOptions(voltage_resolution: float | None = None, capacity_resolution: float | None = None, max_points: int | None = None, interpolation_method: str = 'linear', pre_smoothing: bool = False, diff_smoothing: bool = False, post_smoothing: bool = True, savgol_window_divisor: int = 50, savgol_order: int = 3, voltage_fwhm: float = 0.01, capacity_fwhm: float | None = None, gaussian: GaussianOptions = GaussianOptions(), normalize: Literal['area'] | bool = 'area', normalizing_factor: float | None = None, normalizing_roof: float | None = None, increment_method: Literal['diff'] = 'diff')
The complete recipe for one dQ/dV or dV/dQ transform.
All options for a transform live on one frozen object, passed to
transform_half_cycle or built
implicitly from the **overrides on dqdv /
dvdq.
Attributes:
-
voltage_resolution(float | None) –Voltage step for the q(V) interpolation, e.g.
0.005.Nonekeeps the point count of the input. -
capacity_resolution(float | None) –Capacity step for the V(q) interpolation. Ignored when
max_pointsis set. -
max_points(int | None) –Cap on the V(q) interpolation grid. Takes precedence over
capacity_resolution. -
interpolation_method(str) –Any
kindaccepted byscipy.interpolate.interp1d. -
pre_smoothing(bool) –Savitzky-Golay smoothing of V(q) before inversion.
-
diff_smoothing(bool) –Savitzky-Golay smoothing of q(V) before differentiating.
-
post_smoothing(bool) –Gaussian smoothing of the finished derivative.
-
savgol_window_divisor(int) –Window length divisor for both Savitzky-Golay passes; the window is clamped to at least 3 points and forced odd.
-
savgol_order(int) –Polynomial order for both Savitzky-Golay passes.
-
voltage_fwhm(float) –Full width at half maximum, in volts, of the gaussian post-smoothing — used when differentiating along voltage (dQ/dV).
-
capacity_fwhm(float | None) –The dV/dQ analogue, in capacity units.
Nonederives it as 1% of the capacity span of the half-cycle, which is the same fraction the defaultvoltage_fwhmof 0.01 V represents on a typical ~1 V window. -
gaussian(GaussianOptions) –Remaining
gaussian_filter1dparameters. -
normalize(Literal['area'] | bool) –"area"scales the curve so its integral equals the normalizing factor;Falseleaves it in physical units. The legacyTruespelling of"area"is also accepted. -
normalizing_factor(float | None) –Target for
normalize="area".Noneuses the half-cycle's own end capacity. -
normalizing_roof(float | None) –Rescales the normalizing factor by
end_capacity / normalizing_roof— the hook for normalizing a whole series to one nominal capacity. -
increment_method(Literal['diff']) –Only
"diff"is implemented on this path; the unfinished"hist"binning method lived on the 1.xConverterclass, which was removed in 2.1.
replace ¶
Return a copy with overrides applied (validated).
dqdv ¶
dqdv(source, cycles=None, direction: str = BOTH, options: IcaOptions | None = None, *, strict: bool = False, cycle_mode: str | None = None, number_of_points: int | None = None, **overrides) -> pd.DataFrame
Incremental capacity analysis: dQ/dV against voltage.
Parameters:
-
source–A
CellpyCell, a curve frame fromget_cap(categorical_column=True, label_cycle_number=True), or a(voltage, capacity)pair of arrays. -
cycles–Cycle number or list of cycle numbers.
Noneprocesses all. -
direction(str, default:BOTH) –"charge","discharge"or"both". -
options(IcaOptions | None, default:None) –An
IcaOptions. Defaults toIcaOptions(). -
strict(bool, default:False) –Raise instead of warning when a half-cycle fails.
-
cycle_mode(str | None, default:None) –Overrides the cell's own
cycle_mode, which decides whether the first half-cycle of each cycle is a charge or a discharge. -
number_of_points(int | None, default:None) –Passed to the curve extraction.
-
**overrides–Individual
IcaOptionsfields, for the common case of changing one thing.
Returns:
-
DataFrame–A long frame with columns
cycle,direction,voltage, -
DataFrame–capacity,dqdv.frame.attrscarries the options used, the -
DataFrame–resolved cycle mode, and any per-half-cycle failures.
Example
frame = dqdv(c, cycles=[1, 2], voltage_resolution=0.005) charge = frame[frame.direction == "charge"]
dvdq ¶
dvdq(source, cycles=None, direction: str = BOTH, options: IcaOptions | None = None, *, strict: bool = False, cycle_mode: str | None = None, number_of_points: int | None = None, **overrides) -> pd.DataFrame
Differential voltage analysis (DVA): dV/dQ against capacity.
DVA is the standard technique for electrode balancing and degradation-mode analysis.
It rides the same pipeline as dqdv and is in fact the
simpler of the two: dQ/dV needs the V(q) curve inverted to q(V) before
differentiating, while dV/dQ differentiates the smoothed V(q) curve the
pipeline has already built.
Args and Returns as dqdv, except that the frame's
columns are cycle, direction, capacity, voltage, dvdq,
and that normalization defaults to False: DVA is read from the peak
positions on the capacity axis, so rescaling the ordinate would only
obscure the comparison between cycles.
Example
frame = dvdq(c, cycles=1, direction="charge") frame.plot(x="capacity", y="dvdq")
to_wide ¶
Convert a specced long frame to the wide, cycle-per-column layout.
Wide format is always an explicit conversion, never an implicit mode of
dqdv / dvdq.
Parameters:
-
frame(DataFrame) –
Returns:
-
DataFrame–A frame whose columns are a
(cycle, value)MultiIndex. When the -
DataFrame–input holds both directions the top level is
"<cycle> <direction>", -
DataFrame–so the two do not collide.
transform_half_cycle ¶
transform_half_cycle(voltage, capacity, options: IcaOptions | None = None, *, derivative: str = 'dqdv') -> HalfCycleResult
Transform one half-cycle into dQ/dV or dV/dQ.
This is the pure core: same inputs, same outputs, no shared state. It is
public for the "I already have two arrays" case, where extracting a
half-cycle from a CellpyCell first would be pure overhead.
Parameters:
-
voltage–Voltage samples.
-
capacity–Capacity samples, monotonic and the same length as voltage.
-
options(IcaOptions | None, default:None) –The recipe. Defaults to
IcaOptionsfordqdvand toDVA_DEFAULTS(no normalization) fordvdq. -
derivative(str, default:'dqdv') –"dqdv"or"dvdq".
Returns:
Raises:
-
NullData–If either array is missing, or has one point or fewer.
-
ValueError–If derivative is not a known mode.
Example
capacity, voltage = c.get_ccap(5, as_frame=False) result = transform_half_cycle(voltage, capacity) result.x, result.y # voltage, dQ/dV