linop.FieldCorrected#
- bartorch.linop.FieldCorrected()#
Off-resonance-corrected encoding operator, by time segmentation.
A voxel off resonance by \(f\) accrues a phase \(e^{+i 2 \pi f t}\) by the acquisition time \(t\) of each sample, so the exact operator applies a different transform per sample and is not a single transform at all. Time segmentation approximates it as a short sum of ordinary encodings, each preceded by a spatial weight and followed by a sample weight:
\[A = \sum_{l=1}^{L} \operatorname{diag}(b_l) \, E \, \operatorname{diag}(c_l)\]This is
linop_plusoverlinop_chain, so the result is one BART operator for anyE. It therefore wraps any encoding –CartesianSense(),WaveSense()orNoncartesianSense; the non-Cartesian case corresponds to mirtorch’sGmri.The segment coefficients come from
mri-nufft. Fitting them is a least-squares problem over a histogram of the field map, for which BART has no primitive.- Parameters:
encoding (LinearOperator) – The encoding without off-resonance.
field_map (tensor, default=None) – Off-resonance in Hz, broadcastable to the encoding’s domain.
readout_time (tensor, default=None) – When each sample is taken, in seconds, broadcastable to the encoding’s codomain. Reciprocal units to
field_map.mask (tensor, default=None) – Where the field map is meaningful; everywhere by default. The fit weights the histogram by it, so a mask excluding air concentrates the segments on tissue. A field map with a single value under the mask is rejected: there is nothing to segment, and the correction reduces to a single phase.
segments (int, default=-1) – How many terms the sum has.
-1letsmri-nufftchoose from the spread of the field map and the readout length.method (str, default='svd') –
"svd","mti"or"mfi",mri-nufft’s three factorizations.coefficients (tuple of tensor, default=None) –
(b, c)already computed, of shapes(L, *codomain)and(L, *domain)up to broadcasting. Given these, nothing is fitted andfield_mapis not needed.
Notes
Over a
NoncartesianSensewith no basis, sets or encoding axes, and sample weights varying along the shots and the readout alone, the sum lowers to a single operator: the spatial weights expand the image into one image per segment, and the segments’ sample weights act as a basis over the samples. Its normal is then the Toeplitz one over the basis’s packed Gram, with the spatial weights on either side – a point-spread function per pair of segments rather than two transforms per segment per coil.Over
CartesianSense()with a pattern or a basis, and overWaveSense(), the segments are folded into the coil-slab loop around each slab’s transform instead, and the normal is the forward and adjoint applications; on a grid the closed form would save no transform. Weights varying along the batches, the coils, the sets or the coefficients leave the sum over the whole encoding, as for any other encoding.Examples
>>> E = CartesianSense(maps, (y, x), pattern=mask) >>> A = FieldCorrected(E, b0_hz, readout_time=times, segments=6)