Least-squares Fitting¶
A small registry of analytic models fitted with scipy.optimize.curve_fit.
Each model is selected by a string key, carries its own parameter names, and
ships with a heuristic initial-guess builder so the multi-parameter models
(including the ESEEM decays) converge without hand-tuned start values.
To use the module, import it and create a fitter:
import numpy as np
import atomize.math_modules.least_square_fitting_modules as math_modules
fitter = math_modules.math()
Note
Fitting requires scipy (the math extra: pip install -e .[math]).
fit() raises a RuntimeError if scipy is not installed.
math()¶
Creates the fitter object. All functions below are methods on it.
Available models¶
model_names() returns the keys below. Every model has a
constant baseline term (b or c) that can be fixed at zero with the
no_offset argument of fit().
Basic models
| Model key | Parameters | Function |
|---|---|---|
Linear |
a, b |
\(a x + b\) |
Exponential |
a, k, b |
\(a\,e^{-x/k} + b\) |
Bi-exponential |
a1, k1, a2, k2, b |
\(a_1 e^{-x/k_1} + a_2 e^{-x/k_2} + b\) |
Stretched exponential |
a, k, beta, b |
\(a\,e^{-(x/k)^{\beta}} + b\) |
Gaussian |
a, x0, sigma, b |
\(a\,e^{-(x-x_0)^2/2\sigma^2} + b\) |
Lorentzian |
a, x0, gamma, b |
\(a/(1+((x-x_0)/\gamma)^2) + b\) |
Damped sine |
a, k, f, phi, b |
\(a\,e^{-x/k}\sin(2\pi f x + \varphi) + b\) |
The four Tm + ESEEM … models have longer parameter lists and are described in
the next section.
ESEEM echo-decay models¶
For Hahn-echo \(T_2/T_m\) decays carrying nuclear ESEEM modulation, the models multiply a decay envelope by one or two damped-cosine modulation terms on a constant offset:
The mono-exponential variants drop \(\beta\), and the 1-frequency variants keep only the \(k=1\) modulation term. The four model keys and their parameters:
Tm + ESEEM (stretched, 1 freq)a, Tm, beta, c, m, f, phi, tau_mTm + ESEEM (stretched, 2 freq)a, Tm, beta, c, m1, f1, phi1, tau_m1, m2, f2, phi2, tau_m2Tm + ESEEM (mono-exp, 1 freq)a, Tm, c, m, f, phi, tau_mTm + ESEEM (mono-exp, 2 freq)a, Tm, c, m1, f1, phi1, tau_m1, m2, f2, phi2, tau_m2
Here a scales the envelope, Tm is the decay time, beta the stretch
exponent, c the offset, and each modulation contributes a depth m,
frequency f, phase phi, and damping time tau_m. The modulation
frequencies are seeded automatically from the FFT peaks of the detrended data,
which is what lets the 8–12 parameter fits converge. Frequency units follow the
x-axis: with x in ns, f comes out in 1/ns (GHz); with x in µs, in 1/µs
(MHz).
fit()¶
Fits (x, y) with the named model.
model— one of the keys frommodel_names().guess— optional initial parameter list. IfNoneor the wrong length,default_guess()is used.no_offset— whenTrue, the constant baseline term (borc) is fixed at0and removed from the free parameters, forcing the curve through the baseline instead of floating it.
Returns a dict:
| Key | Description |
|---|---|
y_fit |
Model evaluated at x with the best-fit parameters |
residuals |
y - y_fit |
popt |
Best-fit parameters |
perr |
1-σ parameter errors (sqrt of the covariance diagonal) |
r_squared |
Coefficient of determination |
param_names |
Names matching the popt order (with b/c removed when no_offset=True) |
import numpy as np
import atomize.math_modules.least_square_fitting_modules as math_modules
fitter = math_modules.math()
x = np.linspace(0, 10, 200)
y = 5*np.exp(-x/3.0) + 0.2 + 0.05*np.random.randn(x.size)
res = fitter.fit('Exponential', x, y)
for name, val, err in zip(res['param_names'], res['popt'], res['perr']):
print(f'{name} = {val:.4g} +/- {err:.2g}')
print('R^2 =', res['r_squared'])
model_names()¶
Returns the list of model keys (the first column of the models table).
param_names()¶
Returns the parameter-name list of model (in popt order).
default_guess()¶
Builds a heuristic initial-guess list for model from the data — endpoints for
the decays, the largest peak for Gaussian/Lorentzian, and FFT-seeded
frequencies for the ESEEM models. Pass it (optionally edited) to
fit() as guess.
one_exp_fit()¶
Legacy single-exponential fit kept for existing scripts. curve is a
[x, y] array and guess_array is [a, k, b] for \(a\,e^{-x/k}+b\). Returns the
[x, y_fit] model, the [x, residuals] array, and r_squared. New code should
prefer fit('Exponential', ...).