Skip to content

Math Modules

Helper modules for offline data analysis: least-squares curve fitting, 1D signal processing (apodization, zero filling, smoothing, baseline subtraction, echo-centre detection), FFT and phase correction, and DEER/PDS distance-distribution analysis. They take and return plain NumPy arrays, so a result can be pushed straight to LivePlot with plot_1d() or saved with save_data().

scipy is an optional dependency

The fitting routines, Savitzky–Golay smoothing, and the whole DEER engine require scipy, which is part of the math extra:

pip install -e .[math]

The modules import scipy lazily, so importing them never fails on a minimal install — only the functions that need scipy raise a RuntimeError when it is missing.

Least-squares fitting

import atomize.math_modules.least_square_fitting_modules as math_modules

Function Description
math() Create a fitter exposing the model registry
fit(model, x, y, guess=None, no_offset=False) Fit (x, y) with a named model; returns a result dict
model_names() List the available model keys
param_names(model) Parameter names of a model
default_guess(model, x, y) Heuristic initial guess for a model
one_exp_fit(curve, guess_array) Legacy single-exponential fit

Signal processing

import atomize.math_modules.signal_processing as sigproc

Function Description
apodization_window(n, name, param=8.6) Build an apodization window of length n
zerofill_length(length, choice) Target FFT length for a zero-fill choice
echo_center(envelope, window=0) Echo-centre index from a magnitude envelope
Signal_Processing() Smoothing / baseline / normalization helpers
savitzky_golay(y, window=11, order=3) Savitzky–Golay smoothing (needs scipy)
moving_average(y, window=5) Centred moving-average smoothing
baseline_poly(x, y, order=1, region='all', npts=0) Subtract a polynomial baseline
normalize(y, mode='minmax') Normalize a curve

FFT / phase correction

import atomize.math_modules.fft as fft_module

Function Description
Fast_Fourier() Create the FFT / phase-correction helper
auto_phase_zero(spectrum, threshold=0.1) Zero-order auto-phase (degrees) maximising the magnitude-weighted real part
ph_correction(freq, data_i, data_q, cor1, cor2, cor3) Apply a zero/first/second-order phase polynomial to I+iQ
fft(x_axis, data_i, data_q, sample_spacing, re='False') FFT of I+iQ; magnitude or real/imag parts (ns → MHz)

DEER / PDS analysis

import atomize.math_modules.deer as deer

Distance-distribution analysis for pulsed-dipolar spectroscopy (DEER/PELDOR, RIDME, DQC, SIFTER): background correction + inversion of the orientation-averaged dipolar kernel by Tikhonov/NNLS (GCV or L-curve regularization, sequential or joint DeerLab-style background) or a model-free analytic Mellin transform. Times in µs, distances in nm.

Function Description
deer_invert(t, V, …) One-call pipeline: background-correct → kernel → P(r) (engine/method)
deer_invert_joint(t, V, …) Joint fit of background + λ (λ-pinned) together with P(r)
deer_invert_mellin(t, V, …) Model-free analytic Mellin-transform inversion (auto cutoff, MC CI)
deer_validate(t, V, …) Ensemble validation: background-sweep → median P(r) + uncertainty band
residual_whiteness(resid, …) Residual goodness-of-fit (Durbin–Watson, lag-1 autocorrelation, ACF)
fit_zero_time(t, V, …) Fit the dipolar zero-time t₀ (reference time)
tikhonov_ci(K, F, alpha, P, …) Covariance 95% confidence band on the Tikhonov P(r)
dipolar_kernel(t, r, …) Orientation-averaged kernel K(t, r) (Fresnel closed form)
dipolar_frequency(r, …) Perpendicular dipolar frequency ν⊥(r) = ν_dd/r³
background_fit(t, V, bg_start, bg_end=None, …) Fit intermolecular background on a tail window (sequential)
joint_background(t, V, …) λ-pinned joint background only (coarse, hardened; backs the Mellin engine)
tikhonov_nnls(K, F, alpha, L=None) Non-negative Tikhonov solve K P = F
regularization_matrix(n, order=2) Derivative operator L for smoothing
l_curve(K, F, alphas, L=None, method='gcv') Regularization scan; α by GCV (default) or Menger L-corner
mellin_kernel_spectrum(tau, …) Mellin image Φ(½+iτ) of the dipolar kernel (closed form)
mellin_signal_spectrum(t, F, tau, delta, …) Mellin image Ṽ(½+iτ) of the form factor (δ-split)
mellin_inverse(P_tau, tau, w) Inverse Mellin transform → p(w)
mellin_delta(t, F, level=0.95) Auto Mellin split point δ (F(δ) ≈ 0.95)
default_r_axis(rmin=1.5, rmax=8.0, n=200) Default distance grid (nm)
simulate(t, r, P, …) Forward-simulate a DEER trace from P(r)

Coherence pathways & phase cycling

import atomize.math_modules.coherence_pathways as coh

Pure-Python (no scipy) bookkeeping for pulse-EPR phase cycles: expand short phase-cycle notation, then enumerate every coherence transfer pathway and see which the cycle keeps vs phases out, where each surviving echo lands, and which FIDs survive. Selection rule, not amplitudes (Stoll 2008; Prisner 2016).

Function Description
expand_phase_cycling(recv, *pulse_phases) Expand short notation (x)/[x]/lists/coeffs into per-step pulse + receiver phases
analyze_pathways(recv, pulse_phases, positions, det_pos) Enumerate pathways; classify kept/suppressed, echo positions, FIDs
pathway_report(recv, pulse_phases, positions, det_pos) Ready-to-print summary table of the above
positions_from_taus(taus, base=0.0, grid=None) Cumulative pulse positions from inter-pulse delays (optional grid snap)

Pulse excitation profiles

import atomize.math_modules.pulse_excitation as pe

Excitation/inversion profiles of shaped pulses (rectangular, gaussian, sinc, sine, WURST, sech/tanh) across resonance offset, by full Bloch/propagator spin dynamics of a single S=1/2 — the EasySpin exciteprofile approach, correct for adiabatic pulses, not just the FFT approximation. Pure NumPy (no scipy). Frequencies/offsets in GHz, times in ns; shape params in MHz.

Function Description
excitation_profile(shape, tp, nu1, offsets, params, …) Single-pulse Mx/My/Mz vs offset from an initial state
waveform(shape, t, tp, params) Amplitude envelope + instantaneous frequency of a shape
flip_angle(shape, tp, nu1, params, dt=0.5) On-resonance flip angle (pulse area)
propagate_pulse(M, shape, tp, nu1, offsets, params, …) Apply one pulse to a Bloch-vector array (chain for sequences)
free_evolution(M, offsets, tau) Free precession for tau ns (z-rotation by offset)