Skip to content

relax#

Spin-relaxation analysis for variable-temperature relaxometry.

Turns a series of \(T_1\)/\(T_2\) measurements into molecular dynamics: detect peaks, fit relaxation rates, and invert those rates for the rotational correlation time \(\tau_c\) (how fast a molecule tumbles) using the Bloembergen–Purcell–Pound (BPP) theory of relaxation.

A typical flow is: build the relaxometry spectrum, find the peak windows, fit a rate for each, then feed the temperature-ordered rates to correlation_times.

correlation_times #

correlation_times(
    omega0: float,
    r1_by_temp: ndarray,
    nucleus: str = "1H",
    temperatures_ascending: bool = True,
) -> ndarray | None

Recover the rotational correlation time \(\tau_c\) at each temperature.

Converts a temperature series of longitudinal relaxation rates \(R_1 = 1/T_1\) into the correlation time \(\tau_c\) — the average time a molecule takes to reorient, the headline number for tumbling dynamics.

It works by BPP spectral-density inversion: \(R_1\) peaks (and \(T_1\) is smallest) when tumbling matches the Larmor frequency, i.e. when \(\omega_0\,\tau_c \approx 0.616\). That \(T_1\) minimum anchors the overall scale, and the spectral-density relation is then solved for \(\tau_c = x/\omega_0\) at every temperature. \(^1\)H uses the homonuclear dipolar density, \(^{13}\)C the heteronuclear C–H density.

Parameters:

Name Type Description Default
omega0 float

Larmor angular frequency of the observed nucleus in rad/s, equal to \(2\pi \times \text{SFO1}_{\text{Hz}}\).

required
r1_by_temp ndarray

\(R_1\) values ordered by temperature. Must contain an interior maximum (a \(T_1\) minimum) for the inversion to work.

required
nucleus str

"1H" or "13C" — selects the spectral-density model.

'1H'
temperatures_ascending bool

True if r1_by_temp runs from lowest to highest temperature; used only to pick the fast- vs. slow-tumbling branch on either side of the minimum.

True

Returns:

Type Description
ndarray | None

An array of \(\tau_c\) values in seconds, aligned with r1_by_temp

ndarray | None

or None if \(R_1\) has no interior maximum (no \(T_1\) minimum to

ndarray | None

anchor the inversion). This None acts as a per-peak filter.

Example
# 500 MHz proton Larmor frequency
tau = relax.correlation_times(2 * 3.14159 * 500e6, r1_values, nucleus="1H")

fit_t1 #

fit_t1(
    delays_s: ndarray, intensities: ndarray
) -> float | None

Fit \(T_1\) from a single inversion-recovery curve.

Fits the recovery model

\[I(\tau) = I_0\left(1 - a\,e^{-\tau/T_1}\right) + \text{offset}\]

to one peak's intensities versus delay.

Parameters:

Name Type Description Default
delays_s ndarray

Recovery delays \(\tau\) in seconds.

required
intensities ndarray

Signed (phased, real) peak areas — negative at short delays, recovering to positive.

required

Returns:

Type Description
float | None

\(T_1\) in seconds, or None if the fit fails.

fit_t2 #

fit_t2(
    delays_s: ndarray, intensities: ndarray
) -> float | None

Fit \(T_2\) from a single CPMG decay curve.

Fits the decay model

\[I(t) = I_0\,e^{-t/T_2} + \text{offset}\]

to one peak's intensities versus echo time.

Parameters:

Name Type Description Default
delays_s ndarray

Echo times \(t\) in seconds.

required
intensities ndarray

Peak areas at each echo time.

required

Returns:

Type Description
float | None

\(T_2\) in seconds, or None if the fit fails.

has_t1_minimum #

has_t1_minimum(r1_by_temp: ndarray) -> bool

Check whether a temperature series of \(R_1\) contains a \(T_1\) minimum.

correlation_times can only invert data that brackets the \(T_1\) minimum (an interior maximum in \(R_1\)). Use this to test a peak before attempting the inversion.

Parameters:

Name Type Description Default
r1_by_temp ndarray

\(R_1\) values ordered by temperature.

required

Returns:

Type Description
bool

True if \(R_1\) has an interior maximum within the measured range.

peak_windows #

peak_windows(
    spec: Spectrum2D,
    min_height_ratio: float = 0.08,
    kind: str = "T1",
) -> list[tuple[float, float]]

Find chemical-shift windows around the strong peaks to integrate.

Picks peaks on the reference delay row — the fully-recovered row for \(T_1\), the least-decayed row for \(T_2\) — and returns a window bracketing each, so every peak can be integrated consistently across all delays. The delays must lie on the indirect axis (spec.f1).

Parameters:

Name Type Description Default
spec Spectrum2D

A 2D relaxometry spectrum (from relaxation_spectrum).

required
min_height_ratio float

Ignore peaks shorter than this fraction of the tallest peak (0–1). Raise it to keep only the strongest peaks.

0.08
kind str

"T1" or "T2" — selects which delay row is used as the reference for peak detection.

'T1'

Returns:

Type Description
list[tuple[float, float]]

One (low, high) chemical-shift window per detected peak, in

list[tuple[float, float]]

spectrum x-axis units.

relaxation_rate #

relaxation_rate(
    spec: Spectrum2D, lo: float, hi: float, kind: str = "T1"
) -> float

Fit the relaxation rate \(R = 1/T\) for one peak window.

Integrates the (phased, real) peak area in the window [lo, hi] at every delay and fits the resulting recovery (for \(T_1\)) or decay (for \(T_2\)) curve, returning the rate \(R_1 = 1/T_1\) or \(R_2 = 1/T_2\) in s⁻¹.

Parameters:

Name Type Description Default
spec Spectrum2D

A 2D relaxometry spectrum with delays on spec.f1.

required
lo float

Low edge of the integration window (x-axis units).

required
hi float

High edge of the integration window (x-axis units).

required
kind str

"T1" to fit a recovery curve, "T2" to fit a decay.

'T1'

Returns:

Type Description
float

The relaxation rate in s⁻¹, or NaN if the fit fails.

relaxation_spectrum #

relaxation_spectrum(
    signal: Signal2D, spectrum: Spectrum2D | None = None
) -> Spectrum2D

Build the 2D relaxometry spectrum, one row per relaxation delay.

Lays the relaxation delays along the indirect axis (f1) and chemical shift along the direct axis (f2), so each peak's intensity can be tracked across delays. Pass scan.signal and scan.spectrum; the already-processed spectrum is reused when its indirect axis lines up with the delays (typical for \(^1\)H), otherwise the signal's direct dimension is transformed instead (handles zero-filled processed data such as \(^{13}\)C \(T_1\)).

Parameters:

Name Type Description Default
signal Signal2D

The acquired relaxometry signal (2D).

required
spectrum Spectrum2D | None

The processed spectrum, if already available; recomputed from signal when omitted or unusable.

None

Returns:

Type Description
Spectrum2D

A 2D spectrum with relaxation delays on the indirect (f1) axis, ready

Spectrum2D

for peak_windows and relaxation_rate.

spectral_density #

spectral_density(x: ndarray | float) -> ndarray

Evaluate the \(^1\)H dipolar spectral-density function used for \(R_1\).

The reduced spectral density that governs proton dipolar \(T_1\) relaxation, as a function of \(x = \omega_0\,\tau_c\):

\[g(x) = \frac{x}{1 + x^{2}} + \frac{4x}{1 + 4x^{2}}\]

Exposed mainly for plotting or building custom inversion logic.

Parameters:

Name Type Description Default
x ndarray | float

The product \(\omega_0\,\tau_c\) (scalar or array).

required

Returns:

Type Description
ndarray

\(g(x)\), evaluated elementwise.

t1_minimum_constant #

t1_minimum_constant() -> float

The value of \(\omega_0\,\tau_c\) at the \(T_1\) minimum.

Returns the constant \(x^{*} = \omega_0\,\tau_c \approx 0.616\) that maximises the spectral density spectral_density — the condition marking the shortest \(T_1\). This is the anchor point used by correlation_times.

Returns:

Type Description
float

The dimensionless constant \(x^{*} \approx 0.616\).