models#
Ready-made model functions to fit, grouped by shape.
Pass any of these straight to fitting.fit(data, models.<name>). They cover
the common decay/recovery curves (exponential, Gaussian, Lorentzian,
stretched), general-purpose shapes (power law, sigmoid), and NMR relaxation
dispersion models (CPMG two-/three-site exchange). See each function for its
formula and parameters.
bi_exponential
#
bi_exponential(
x: ndarray,
a1: float,
tau1: float,
a2: float,
tau2: float,
offset: float,
) -> ndarray
Two-component (bi-exponential) decay.
A sum of two single decays — the go-to model when a sample has two distinct pools relaxing at different rates (e.g. bound vs. free liquid), giving a fast initial drop followed by a slower tail.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Independent variable, usually time. |
required |
a1
|
float
|
Amplitude of the first (typically faster) component, \(A_1\). |
required |
tau1
|
float
|
Time constant of the first component, \(\tau_1\). |
required |
a2
|
float
|
Amplitude of the second (typically slower) component, \(A_2\). |
required |
tau2
|
float
|
Time constant of the second component, \(\tau_2\). |
required |
offset
|
float
|
Baseline the curve decays towards, \(c\). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted \(y\) values, one per element of |
Notes
The two components are exchangeable: swapping \((A_1, \tau_1)\) with
\((A_2, \tau_2)\) gives a bit-identical curve, so a fitter may return
the same physical answer with the labels either way round. Pass the
fitted vector through canonicalize_bi_exponential (or
_utils.canonicalize_component_order) before reporting, so that
"component 1" always means the faster one.
Both time constants are clamped away from zero (sign preserved) and the exponents are clipped, matching the rest of this module.
cpmg_sdr
#
Static Dephasing Regime (SDR) model for magnetic-particle solutions.
Applies when particles are large enough that the strong local field gradients around them are not averaged out by molecular diffusion. In this regime the relaxation rate no longer depends on the CPMG frequency, so the model returns a flat (frequency-independent) line — a useful null reference when checking whether a sample shows true dispersion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nu_cpmg
|
ndarray
|
CPMG frequency. Only its shape is used (the result is constant across it). |
required |
r2_0
|
float
|
Intrinsic relaxation rate, \(R_2^{0}\). |
required |
gamma_g2_tau_d
|
float
|
The combined dephasing term \(\gamma^{2} G^{2} \tau_D\) (gyromagnetic ratio, local gradient, and correlation time rolled into one fitted constant). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Effective \(R_2\) values (a constant float64 array matching the |
ndarray
|
shape of |
ndarray
|
|
ndarray
|
rate to a whole number. |
cpmg_three_site
#
cpmg_three_site(
nu_cpmg: ndarray,
r2_0: float,
d_omega_ab: float,
d_omega_ac: float,
d_omega_bc: float,
k_eff: float,
p_a: float,
p_b: float,
) -> ndarray
Three-site CPMG dispersion — empirical Lorentzian approximation.
Extends the two-site picture to a nucleus exchanging between three environments — for systems with an intermediate state, where each pair of sites contributes its own shift difference and population product.
Warning
This is a phenomenological curve-shape, not a physical model, and
it is retained under that understanding rather than as a derivation.
There is no exact closed form for three-site CPMG dispersion in
routine use: the secular equation is formally solvable for \(N \le 4\)
sites but the resulting expressions are unusable in practice
(Koss, Rance & Palmer, J. Magn. Reson. 321, 106846, 2020), so
established software (relax NS models, ChemEx, CATIA) performs
numerical Bloch–McConnell propagation instead. Published
approximate three-site forms do exist — Koss, Rance & Palmer,
Biochemistry 57, 4753–4763 (2018), Eq. 33 of the 2020 JMR paper,
and the geometric approximation of Chao & Byrd, J. Magn. Reson.
277, 8–14 (2017) — and none of them is this expression.
Consequences for interpretation: the denominator \(k_{\text{eff}} + \nu^{2}/k_{\text{eff}}\) mixes a rate in s⁻¹ with a frequency in Hz and is not dimensionally coherent, so \(k_{\text{eff}}\) is a shape parameter with no defensible physical meaning. It does have the right qualitative behaviour — a plateau of \(R_2^{0} + \Sigma/k_{\text{eff}}\) at \(\nu \to 0\) decaying to \(R_2^{0}\) as \(\nu \to \infty\) — which makes it usable for empirical curve description and for deciding whether a dataset shows dispersion at all. Do not quote fitted \(k_{\text{eff}}\) or \(\Delta\omega\) values from it as exchange parameters; fit a Bloch–McConnell model for that.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nu_cpmg
|
ndarray
|
CPMG refocusing frequency \(\nu_{\text{CPMG}}\) (the swept variable). |
required |
r2_0
|
float
|
Intrinsic relaxation rate with no exchange, \(R_2^{0}\). |
required |
d_omega_ab
|
float
|
Shift difference between sites A and B, \(\Delta\omega_{AB}\). |
required |
d_omega_ac
|
float
|
Shift difference between sites A and C, \(\Delta\omega_{AC}\). |
required |
d_omega_bc
|
float
|
Shift difference between sites B and C, \(\Delta\omega_{BC}\). |
required |
k_eff
|
float
|
Effective overall exchange-rate shape parameter, \(k_{\text{eff}}\) (see the warning above). |
required |
p_a
|
float
|
Fractional population of site A, \(p_A\). |
required |
p_b
|
float
|
Fractional population of site B, \(p_B\) (site C is \(1 - p_A - p_B\)). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Effective \(R_2\) values, one per element of |
cpmg_tollinger
#
cpmg_tollinger(
nu_cpmg: ndarray,
r2_0: float,
delta_omega: float,
k_ex: float,
k_ba: float,
) -> ndarray
Two-site exchange dispersion for the slow-exchange limit (Tollinger).
An alternative to r2_effective_carver_richards that is accurate
when exchange is slow compared with the shift difference. Use it for CPMG
dispersion data where the populations are skewed and the simple
fast-exchange form breaks down.
The bracket is \(1 - \operatorname{sinc}(\Delta\omega\tau_{cp})\), and \(\operatorname{sinc}(0) = 1\), so as \(\Delta\omega\,\tau_{cp} \to 0\) — very fast pulsing, or a vanishing shift difference — the model correctly returns \(R_2^{0}\): exchange is fully refocused and contributes nothing. (A previous implementation guarded only the denominator against zero and so returned \(R_2^{0} + k_{AB}\) in that limit, i.e. maximal broadening exactly where there should be none.)
Reference: Tollinger et al., J. Am. Chem. Soc. 2001, 123, 11341.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nu_cpmg
|
ndarray
|
CPMG refocusing frequency \(\nu_{\text{CPMG}}\) (the swept variable). |
required |
r2_0
|
float
|
Intrinsic relaxation rate with no exchange, \(R_2^{0}\). |
required |
delta_omega
|
float
|
Chemical-shift difference between the two sites, \(\Delta\omega\) (rad/s). |
required |
k_ex
|
float
|
Total exchange rate, \(k_{\text{ex}}\). |
required |
k_ba
|
float
|
Reverse (B→A) exchange rate, \(k_{BA}\); the forward rate is \(k_{AB} = k_{\text{ex}} - k_{BA}\). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Effective \(R_2\) values, one per element of |
cpmg_two_site
#
cpmg_two_site(
nu_cpmg: ndarray,
r2_0: float,
delta_omega: float,
k_ex: float,
p_a: float,
) -> ndarray
Two-site exchange CPMG relaxation dispersion — the Carver–Richards closed form.
Fits how the effective transverse relaxation rate \(R_2^{\text{eff}}\) changes with the CPMG pulsing frequency when a nucleus hops between two environments (e.g. a protein switching between two conformations). The rate is highest when slow pulsing lets exchange dephase the signal and drops as fast pulsing refocuses it — the classic dispersion curve used to extract exchange rates and populations.
This is the general two-site solution: it makes no assumption about
the exchange regime, unlike r2_effective_luz_meiboom (fast exchange
only). The reduced form is used, i.e. both sites share one intrinsic
rate \(R_2^{0}\):
with
Conventions (these matter — getting them wrong changes every fitted number): \(\nu_{\text{CPMG}} = 1/(4\tau_{cp})\) in Hz, where \(\tau_{cp}\) is the delay between the centres of successive \(180°\) pulses divided by two, i.e. the CPMG block is \([\tau_{cp} - 180° - 2\tau_{cp} - 180° - \tau_{cp}]^{n}\) and \(\nu_{\text{CPMG}} = n / T_{\text{rel}}\). \(\Delta\omega\) and \(k_{\text{ex}}\) are in rad/s; \(R_2\) in s⁻¹. The equation is not symmetric under swapping the sites — \(p_A\) must be the major population (\(p_A > 0.5\)).
References
Carver & Richards, J. Magn. Reson. 6, 89–105 (1972),
doi:10.1016/0022-2364(72)90090-X; numerically tractable form from the
appendix of Davis, Perlman & London, J. Magn. Reson. B 104,
266–275 (1994). Transcription cross-checked against the reference
implementation lib/dispersion/cr72.py in relax
(https://www.nmr-relax.com/manual/The_reduced_CR72_2_site_CPMG_model.html)
and validated numerically against Bloch–McConnell propagation.
Warning
Prior versions of this function implemented an ad-hoc Lorentzian \(\Delta\omega^{2} k_{\text{ex}} p_A p_B / (2(k_{\text{ex}}^{2}+\nu^{2}))\) that is neither Carver–Richards nor any published limit of it, and which mixed rad/s with Hz in its denominator. Exchange parameters fitted with that expression are not comparable with these.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nu_cpmg
|
ndarray
|
CPMG refocusing frequency \(\nu_{\text{CPMG}}\) in Hz (the swept variable). |
required |
r2_0
|
float
|
Intrinsic relaxation rate with no exchange, \(R_2^{0}\) (s⁻¹). |
required |
delta_omega
|
float
|
Chemical-shift difference between the two sites, \(\Delta\omega\) (rad/s). |
required |
k_ex
|
float
|
Total exchange rate \(k_{\text{ex}} = k_{AB} + k_{BA}\) (s⁻¹). |
required |
p_a
|
float
|
Fractional population of the major site A, \(p_A\) (site B is \(1 - p_A\)). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Effective \(R_2\) values, one per element of |
double_sigmoid
#
double_sigmoid(
x: ndarray,
a1: float,
c1: float,
w1: float,
a2: float,
c2: float,
w2: float,
offset: float,
) -> ndarray
Two stacked sigmoids for a two-step transition.
Sums two S-shaped curves, so it can fit data that switches levels twice — e.g. a system with two sequential transitions or two unfolding events.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Independent variable. |
required |
a1
|
float
|
Height of the first step, \(A_1\). |
required |
c1
|
float
|
Midpoint of the first step, \(c_1\). |
required |
w1
|
float
|
Steepness of the first step, \(w_1\). |
required |
a2
|
float
|
Height of the second step, \(A_2\). |
required |
c2
|
float
|
Midpoint of the second step, \(c_2\). |
required |
w2
|
float
|
Steepness of the second step, \(w_2\). |
required |
offset
|
float
|
Lower plateau the curve rises from, \(c\). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted \(y\) values, one per element of |
exponential_decay
#
Single-rate exponential decay.
The workhorse for anything that falls from a starting level to a flat baseline at one characteristic rate: transverse (\(T_2\)) relaxation, a free-induction-decay envelope, or a generic intensity that dies away.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Independent variable, usually time. |
required |
amplitude
|
float
|
Starting height above the baseline, \(A\). |
required |
time_constant
|
float
|
Decay constant \(\tau\) (the value of \(x\) at which the signal falls to \(1/e \approx 37\%\) of \(A\)). |
required |
offset
|
float
|
Baseline the curve decays towards, \(c\). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted \(y\) values, one per element of |
exponential_plateau
#
exponential_plateau(
x: ndarray,
amplitude: float,
time_constant: float,
offset: float,
) -> ndarray
Exponential build-up to a plateau (saturation-recovery \(T_1\)).
Mirror image of a decay: the signal grows from the baseline and levels off at \(A + c\). This is the saturation-recovery longitudinal-relaxation curve, where magnetisation rebuilds along the field after being saturated.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Independent variable, usually the recovery delay. |
required |
amplitude
|
float
|
Total rise from baseline to plateau, \(A\). |
required |
time_constant
|
float
|
Recovery constant \(\tau\) (\(T_1\)); the signal reaches \(\approx 63\%\) of its final rise at \(x = \tau\). |
required |
offset
|
float
|
Baseline the curve starts from, \(c\). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted \(y\) values, one per element of |
gaussian_decay
#
Gaussian (bell-shaped) decay.
Falls off faster than an exponential at first and then more gently — the shape produced by a spread of frequencies rather than a single rate. Typical of strongly-coupled solids and inhomogeneously broadened signals, where it gives a Gaussian line in the spectrum.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Independent variable, usually time. |
required |
amplitude
|
float
|
Starting height above the baseline, \(A\). |
required |
time_constant
|
float
|
Decay constant \(\tau\) (sets the width of the bell). |
required |
offset
|
float
|
Baseline the curve decays towards, \(c\). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted \(y\) values, one per element of |
gaussian_lorentzian
#
gaussian_lorentzian(
x: ndarray,
a_gauss: float,
tau_gauss: float,
a_lorentz: float,
tau_lorentz: float,
offset: float,
) -> ndarray
Combined Gaussian + Lorentzian decay (Voigt-like).
Adds a Gaussian and an exponential (Lorentzian) decay together, so it can capture signals that are partly inhomogeneously broadened (Gaussian) and partly homogeneously broadened (Lorentzian) — the time-domain analogue of a Voigt lineshape. Useful when neither pure shape fits the data alone.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Independent variable, usually time. |
required |
a_gauss
|
float
|
Amplitude of the Gaussian part, \(A_g\). |
required |
tau_gauss
|
float
|
Time constant of the Gaussian part, \(\tau_g\). |
required |
a_lorentz
|
float
|
Amplitude of the Lorentzian (exponential) part, \(A_l\). |
required |
tau_lorentz
|
float
|
Time constant of the Lorentzian part, \(\tau_l\). |
required |
offset
|
float
|
Baseline the curve decays towards, \(c\). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted \(y\) values, one per element of |
lorentzian_decay
#
Lorentzian decay (exponential in time, Lorentzian in the spectrum).
A plain exponential decay in the time domain Fourier-transforms into a
Lorentzian line in the spectrum, so this is the model for homogeneously
broadened signals with a single \(T_2\). It is identical in form to
ExponentialModels.exponential_decay; the name reflects the
lineshape it produces, not a different equation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Independent variable, usually time. |
required |
amplitude
|
float
|
Starting height above the baseline, \(A\). |
required |
time_constant
|
float
|
Decay constant \(\tau\) (\(T_2\)); sets the linewidth of the resulting Lorentzian. |
required |
offset
|
float
|
Baseline the curve decays towards, \(c\). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted \(y\) values, one per element of |
power_law
#
Power-law relationship.
For quantities that scale as a power of \(x\) rather than rising or decaying exponentially — e.g. signal attenuation versus gradient strength, or any process that looks like a straight line on a log–log plot.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Independent variable. |
required |
amplitude
|
float
|
Overall scale factor, \(A\). |
required |
exponent
|
float
|
Power \(p\) (negative for a falling curve, positive for a rising one). |
required |
offset
|
float
|
Vertical offset, \(c\). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted \(y\) values, one per element of |
Notes
x is cast to float first. np.power on an integer array
raises outright for a negative integer exponent ("Integers to
negative integer powers are not allowed") and silently truncates for
a positive one — the same class of dtype-inheritance defect that
made the SDR relaxation model return integer rates.
sigmoid
#
Sigmoid (S-shaped) transition.
Describes a smooth step from one level to another: titration and dose–response curves, melting/transition profiles, or any quantity that switches between two plateaus around a threshold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Independent variable. |
required |
amplitude
|
float
|
Total height of the step, \(A\). |
required |
center
|
float
|
Midpoint \(x_0\) where the curve is halfway through the transition. |
required |
width
|
float
|
Steepness \(w\); smaller is a sharper step, larger is more gradual. |
required |
offset
|
float
|
Lower plateau the curve rises from, \(c\). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted \(y\) values, one per element of |
stretched_exponential
#
stretched_exponential(
x: ndarray,
amplitude: float,
time_constant: float,
beta: float,
offset: float,
) -> ndarray
Stretched exponential (Kohlrausch–Williams–Watts).
A decay that is not governed by a single rate but by a distribution of rates — typical of disordered, glassy, or porous systems where many environments relax at slightly different speeds. The stretch exponent \(\beta\) controls how broad that distribution is (\(\beta = 1\) recovers an ordinary single exponential).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Independent variable, usually time. |
required |
amplitude
|
float
|
Starting height above the baseline, \(A\). |
required |
time_constant
|
float
|
Characteristic time \(\tau\). |
required |
beta
|
float
|
Stretch exponent, \(0 < \beta \le 1\); smaller means a wider spread of underlying rates. |
required |
offset
|
float
|
Baseline the curve decays towards, \(c\). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted \(y\) values, one per element of |
Notes
\((x/\tau)^{\beta}\) is only real for \(x \ge 0\), and blows up at
\(x = 0\) when \(\beta < 0\). Negative x is therefore clipped to
zero, \(\tau\) is clamped away from zero, and the exponent is clipped
before exp so the model returns finite numbers for every
parameter an optimizer might try.