Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Domain-Agnostic Autophase

Phase correction is a frequency-domain operation, but the data you have in hand is often a time-domain FID. Classically that forces you to remember the ritual:

spectrum = fid.xmr.to_spectrum().xmr.autophase()   # you must FFT first, by hand

autophase now removes that ceremony — hand it a FID and it Fourier-transforms into the frequency domain for you, then phases:

spectrum = fid.xmr.autophase()   # auto-FFT happens under the hood

This is powered by the domain-contract taxonomy: a gate decorator plus two domain decorators sharing one engine, each declaring a function’s contract at the definition site.

TierDecoratorContractCost
gate@requires_attrs(...)raises if metadata is missingO(1)O(1)
domain — funnel@ensures_domain(...)transforms into the home domain, result stays thereO(Nlog⁡N)O(N \log N)
domain — preserving@computes_in(...)round-trips through the home domain, representation restoredO(Nlog⁡N)O(N \log N)

Both domain decorators also resolve the working axis: a dim=None argument is filled with the spectral dimension actually present (frequency [Hz] or chemical_shift [ppm]) — an explicit dim is never overridden.

autophase is a funnel operation — you phase in order to inspect the spectrum, so the result lands there:

A FID s(t)s(t) and its spectrum S(ν)S(\nu) are a Fourier pair, S(ν)=F{s(t)}S(\nu) = \mathcal{F}\{s(t)\}, and phase correction acts on the spectrum,

Sphased(ν)=S(ν) exp⁡ ⁣[i(p0+p1 ν−νpivotΔν)].S_\text{phased}(\nu) = S(\nu)\, \exp\!\left[i\left(p_0 + p_1\,\frac{\nu - \nu_\text{pivot}}{\Delta\nu}\right)\right].

@ensures_domain supplies the F\mathcal{F} so you can start from either side.

Imports & a small plotting helper

Hand it a raw FID

We simulate a noisy, time-domain FID with a 65° zero-order phase error baked in. Note the dimension: this is a FID, not a spectrum.

('time',)

Now call autophase directly on the FID. There is no to_spectrum() in the chain — @ensures_domain performs the FFT, and the output comes back as a spectrum.

input FID : ('time',)
phased    : ('frequency',) | p0 = -60.2°, p1 = 89.2°

The distorted spectrum has its signal smeared between the real (absorptive) and imaginary (dispersive) channels; after autophasing the peaks stand up cleanly in the real part.

<Figure size 700x480 with 2 Axes>

Already a spectrum? No extra FFT.

The same call on data that is already spectral is a no-op on the domain — the ensures tier sees a spectral dimension and passes the array straight through, so nothing is transformed twice.

input : ('frequency',) -> output: ('frequency',)

Whether you start from the FID or the spectrum, autophase does the right thing — the domain plumbing is handled for you, and the output honestly reports where it landed.