bartorch.linop.FieldCorrected

Contents

bartorch.linop.FieldCorrected#

bartorch.linop.FieldCorrected(encoding, field_map=None, readout_time=None, *, mask=None, segments=-1, method='svd', coefficients=None)#

An encoding with off-resonance during the readout, by time segmentation.

A voxel off resonance by f accrues a phase exp(-i 2 pi f t) by the time t of each sample, so the exact operator is a different transform per sample and no transform at all in the usual sense. Time segmentation approximates it as a short sum of ordinary encodings, each with a spatial weight before it and a sample weight after:

A = sum_l diag(b_l) . E . diag(c_l)

which is linop_plus over linop_chain: one BART operator, whatever E is. So this wraps any encoding – CartesianSense(), WaveSense(), or NoncartesianSense – and the last of those is what mirtorch calls Gmri.

The coefficients are mri-nufft’s: the fit is a least-squares problem over a histogram of the field map, not something BART has a primitive for and not something to write twice.

Parameters:
  • encoding (LinearOperator) – The encoding without off-resonance.

  • field_map (tensor) – Off-resonance in Hz, broadcastable to the encoding’s domain.

  • readout_time (tensor) – When each sample is taken, in seconds, broadcastable to the encoding’s codomain. Reciprocal units to field_map.

  • mask (tensor, optional) – Where the field map is meaningful; everywhere by default. The fit weights the histogram by it, so a mask that excludes air spends the segments on tissue. A map with a single value under the mask is refused: there is nothing to segment, and the correction is one phase.

  • segments (int) – How many terms the sum has. -1 lets mri-nufft choose from the spread of the field map and the readout length.

  • method (str) – "svd", "mti" or "mfi", mri-nufft’s three factorizations.

  • coefficients (tuple of tensor, optional) – (b, c) already computed, of shapes (L, *codomain) and (L, *domain) up to broadcasting. Given these, nothing is fitted and field_map is not needed.

Notes

The normal operator is the sum applied twice rather than anything cheaper: a segmented encoding has no Toeplitz form, because the spatial weights do not commute with the transform. More segments is a better approximation and proportionally more work.

Examples

>>> E = CartesianSense(maps, (coils, y, x), pattern=mask)
>>> A = FieldCorrected(E, b0_hz, readout_time=times, segments=6)