Version 0.3.5

Release Date: February 22, 2026

Status: Stable release

Overview

EPyR Tools v0.3.5 delivers major improvements to lineshape derivative fitting and visualization defaults. The Voigt and pseudo-Voigt profiles now use analytical derivatives, making fit_epr_signal() reliable for first and second derivative EPR spectra. Plotting functions have been refined with thinner lines, better color scales, and compatibility with recent matplotlib versions.

Lineshape Fitting Improvements

Analytical Voigt Derivatives

Previous versions computed Voigt derivatives numerically using np.gradient. During fitting, scipy.optimize.curve_fit also evaluates the Jacobian numerically, resulting in a numerical derivative of a numerical derivative. This compounded error prevented convergence for derivative Voigt signals.

v0.3.5 replaces the numerical approach with analytical derivatives of the Faddeeva function:

\[w'(z) = -2z \cdot w(z) + \frac{2i}{\sqrt{\pi}}\]
\[w''(z) = -2w(z) + 4z^2 \cdot w(z) - \frac{4iz}{\sqrt{\pi}}\]

where \(z = (x - x_0 + i\gamma) / (\sigma\sqrt{2})\) and \(w(z)\) is the Faddeeva function (scipy.special.wofz).

Impact: Voigt derivative fitting now converges reliably with R² > 0.99 on synthetic data.

Pseudo-Voigt Derivative and Phase Support

The pseudo_voigt lineshape in fit_epr_signal() previously ignored the derivative parameter and emitted a false warning. The internal lshape() function was also using a buggy Dawson function approximation that returned complex values when phase != 0.

v0.3.5 fixes both issues:

  • derivative and phase are now passed directly to pseudo_voigt()

  • lshape() delegates to the canonical gaussian() and lorentzian() functions, which handle derivatives and phase analytically

Initial Parameter Estimation for Derivative Signals

The automatic parameter estimator assumed absorption-like data (peak at center). For first derivative signals, this produced incorrect estimates (e.g., amplitude of 2.5 instead of 500, width of 117 instead of 15).

New estimation heuristics for derivative signals:

  • 1st derivative: center at zero-crossing between extrema, width from peak separation, amplitude calibrated against unit-amplitude model

  • 2nd derivative: center at central extremum, width from side lobe separation, amplitude from model comparison

Example usage:

from epyr.lineshapes import fit_epr_signal

# Fit 1st derivative Voigt - now works with auto parameter estimation
result = fit_epr_signal(x, y, 'voigt', derivative=1)

# Fit pseudo-Voigt with phase
result = fit_epr_signal(x, y, 'pseudo_voigt', derivative=1, fit_phase=True)

# Fine-tune optimizer settings
result = fit_epr_signal(x, y, 'voigt', derivative=1,
                        maxfev=50000, ftol=1e-12)

Visualization Improvements

plot_2d_map

  • Fixed colorbar crash with matplotlib 3.8+ (replaced tight_layout with constrained layout)

  • Added vmin and vmax parameters for color scale control

import epyr

# Symmetric color scale
m = np.max(np.abs(y))
epyr.plot_2d_map(x, y, parm, vmin=-m, vmax=m, cmap="RdBu_r")

plot_1d and plot_2d_waterfall

  • Default line width set to 0.75 (was ~1.5 matplotlib default)

  • plot_2d_waterfall default max_traces reduced from 50 to 20

  • plot_2d_waterfall accepts a lw parameter for custom line width

JPG Export

  • Default DPI increased from 150 to 200

  • 2D map colormap changed to RdBu_r (diverging, suitable for EPR signals)

  • Color scale uses symmetric bounds from np.percentile(np.abs(y), 98)

Bug Fixes

  • baseline_polynomial_2d: Fixed meshgrid shape mismatch. np.meshgrid arguments were swapped, producing arrays of shape (nx, ny) instead of (ny, nx) matching the data.

Upgrade Guide

No breaking changes. All existing code continues to work. The only visible differences are:

  • Thinner lines in plots (set lw=1.5 explicitly to restore previous width)

  • Fewer traces in waterfall plots (set max_traces=50 to restore)

  • Different JPG export appearance (colormap, DPI, color scale)