mrphy.mobjs

mrphy.mobjs

Classes for MRI excitation simulations

mrphy.mobjs.Pulse

class mrphy.mobjs.Pulse(rf: Tensor | None = None, gr: Tensor | None = None, *, dt: Tensor = tensor(4.0000e-06, dtype=torch.float64), gmax: Tensor = tensor(5., dtype=torch.float64), smax: Tensor = tensor(12000., dtype=torch.float64), rfmax: Tensor = tensor(0.2500, dtype=torch.float64), desc: str = 'generic pulse', device: device = device(type='cpu'), dtype: dtype = torch.float32)

Pulse object of RF and GR

Usage:

pulse = Pulse(rf, gr, *, dt, gmax, smax, rfmax, desc, device,`` dtype)``

Inputs:
  • rf: (N,xy, nT,(nCoils)) “Gauss”, xy for separating real and imag part.

  • gr: (N,xyz,nT), “Gauss/cm”

  • dt: ()(N ⊻ 1,), “Sec”, dwell time.

  • gmax: ()(N ⊻ 1, xyz ⊻ 1), “Gauss/cm”, max |gradient|.

  • smax: ()(N ⊻ 1, xyz ⊻ 1), “Gauss/cm/Sec”, max |slew rate|.

  • rfmax: ()(N ⊻ 1,(nCoils)), “Gauss”, max |RF|.

  • desc: str, an description of the pulse to be constructed.

  • device: torch.device.

  • dtype: torch.dtype.

Properties:
  • device

  • dtype

  • is_cuda

  • shape: (N,1,nT)

  • gmax: (N ⊻ 1, xyz), “Gauss/cm”, max |gradient|.

  • smax: (N ⊻ 1, xyz), “Gauss/cm/Sec”, max |slew rate|.

  • rfmax: (N ⊻ 1,(nCoils)), “Gauss”, max |RF|.

  • rf: (N,xy, nT,(nCoils)), “Gauss”, xy for separating real and imag part.

  • gr: (N,xyz,nT), “Gauss/cm”

  • dt: (N ⊻ 1,), “Sec”, dwell time.

  • desc: str, an description of the pulse to be constructed.

asdict(*, toNumpy: bool = True) dict

Convert mrphy.mobjs.Pulse object to dict

Usage:

d = pulse.asdict(*, toNumpy)

Inputs:
  • toNumpy: [T/f], convert Tensor to Numpy arrays.

Outputs:
  • d: dict, dictionary with detached data identical to the object.

beff(loc: Tensor, *, Δf: Tensor | None = None, b1Map: Tensor | None = None, γ: Tensor = tensor(4257.6000, dtype=torch.float64)) Tensor

Compute B-effective of provided location from the pulse

Usage:

beff = pulse.beff(loc, *, Δf, b1Map, γ)

Inputs:
  • loc: (N,*Nd,xyz), “cm”, locations.

Optionals:
  • Δf: (N,*Nd,), “Hz”, off-resonance.

  • b1Map: (N,*Nd,xy,(nCoils)), a.u., transmit sensitivity.

  • γ: (N,*Nd), “Hz/Gauss”, gyro-ratio

Outputs:
  • beff: (N,*Nd,xyz,nT)

interpT(dt: Tensor, *, kind: str = 'linear') Pulse

Interpolate pulse of dt by kind.

Usage:

new_pulse = pulse.interpT(dt, *, kind)

Inputs:
  • dt: (1,), “Sec”, new simulation dwell time.

  • kind: str, passed to scipy.interpolate.interp1d.

Outputs:
  • new_pulse: mrphy.mobjs.Pulse object.

Note

This method requires both dt and self.dt to be unique/global, i.e., of shape (1,), which ensures pulse length to be the same within a batch after interpolation.

to(*, device: device = device(type='cpu'), dtype: dtype = torch.float32) Pulse

Duplicate the object to the prescribed device with dtype

Usage:

new_pulse = pulse.to(*, device, dtype)

Inputs:
  • device: torch.device

  • dtype: torch.dtype

Outputs:
  • new_pulse: mrphy.mobjs.Pulse object.

mrphy.mobjs.SpinArray

class mrphy.mobjs.SpinArray(shape: tuple, mask: Tensor | None = None, *, T1: Tensor | None = None, T1_: Tensor | None = None, T2: Tensor | None = None, T2_: Tensor | None = None, γ: Tensor | None = None, γ_: Tensor | None = None, M: Tensor | None = None, M_: Tensor | None = None, device: device = device(type='cpu'), dtype: dtype = torch.float32)

mrphy.mobjs.SpinArray object

Usage:

spinarray = SpinArray(shape, mask, *, T1_, T2_, γ_, M_, device,`` dtype)`` spinarray = SpinArray(shape, mask, *, T1, T2, γ, M, device, dtype)

Inputs:
  • shape: tuple, e.g., (N, nx, ny, nz).

Optionals:
  • mask: (1, *Nd), where does compact attributes locate in Nd.

  • T1T1_: (N, *Nd ⊻ nM), “Sec”, T1 relaxation coeff.

  • T2T2_: (N, *Nd ⊻ nM), “Sec”, T2 relaxation coeff.

  • γγ_: (N, *Nd ⊻ nM), “Hz/Gauss”, gyro ratio.

  • MM_: (N, *Nd ⊻ nM, xyz), spins, equilibrium [0 0 1].

  • device: torch.device.

  • dtype: torch.dtype

Properties:
  • shape: (N, *Nd).

  • mask: (1, *Nd).

  • device.

  • dtype.

  • ndim: len(shape)

  • nM: nM = torch.count_nonzero(mask).item().

  • T1_: (N, nM), “Sec”, T1 relaxation coeff.

  • T2_: (N, nM), “Sec”, T2 relaxation coeff.

  • γ_: (N, nM), “Hz/Gauss”, gyro ratio.

  • M_: (N, nM, xyz), spins, equilibrium [0 0 1]

Warning

  • Do NOT modify the mask of an object, e.g., spinarray.mask[0] = True.

  • Do NOT proceed indexed/masked assignments over any non-compact attribute, e.g., spinarray.T1[0] = T1G or spinarray.T1[mask] = T1G. The underlying compact attributes will NOT be updated, since they do not share memory. The only exception is when torch.all(mask == True) and the underlying compact is contiguous, where the non-compact is just a view((N, *Nd, ...)). Checkout crds_() and mask_() for indexed/masked access to compacts.

Tip

  • mask is GLOBAL for a batch, in other words, one cannot specify distinct masks w/in a batch. This design is to reduce storage/computations in, e.g., applypulse (blochsim), avoiding extra allocations. For DNN applications where an in-batch variation of mask may seemingly be of interest, having torch.all(mask == True) and postponing the variations to eventual losses evaluation can be a better design, which allows reuse of M_, etc., avoiding repetitive allocations.

applypulse(pulse: Pulse, *, doEmbed: bool = False, doRelax: bool = True, doUpdate: bool = False, loc: Tensor | None = None, loc_: Tensor | None = None, Δf: Tensor | None = None, Δf_: Tensor | None = None, b1Map: Tensor | None = None, b1Map_: Tensor | None = None) Tensor

Apply a pulse to the spinarray object

Typical usage:

M = spinarray.applypulse(pulse, *, loc, doEmbed=True, doRelax,`` doUpdate, Δf, b1Map)`` M_ = spinarray.applypulse(pulse, *, loc_, doEmbed=False, `` \ ``doRelax, doUpdate, Δf_, b1Map_)

Inputs:
  • pulse: mrphy.mobjs.Pulse.

  • locloc_: (N,*Nd ⊻ nM,xyz), “cm”, locations.

Optionals:
  • doEmbed: [t/F], return M or M_

  • doRelax: [T/f], do relaxation during Bloch simulation.

  • doUpdate: [t/F], update self.M_

  • Δf``⊻ ``Δf_: (N,*Nd ⊻ nM), “Hz”, off-resonance.

  • b1Mapb1Map_: (N,*Nd ⊻ nM,xy,(nCoils)), transmit sensitivity.

Outputs:
  • MM_: (N,*Nd ⊻ nM,xyz)

Note

When doUpdate == True and doEmbed == False, the output compact magnetization Tensor is a reference to self.M_, and needs caution when being accessed.

asdict(*, toNumpy: bool = True, doEmbed: bool = True) dict

Convert mrphy.mobjs.SpinArray object to dict

Usage:

d = spinarray.asdict(*, toNumpy, doEmbed)

Inputs:
  • toNumpy: [T/f], convert Tensor to Numpy arrays.

  • doEmbed: [T/f], embed compactly stored (nM) data to the mask (*Nd).

Outputs:
  • d: dict, dictionary with detached data identical to the object.

crds_(crds: list) list

Compute crds for compact attributes

Data in a SpinArray object is stored compactly, such that only those correspond to 1 on the spinarray.mask is kept. This function is provided to facilitate indexing the compact data from regular indices, by computing (ix, iy, iz) -> iM

Usage:

crds_ = spinarray.crds_(crds)

Inputs:
  • crds: indices for indexing non-compact attributes.

Outputs:
  • crds_: list, len(crds_) == 2+len(crds)-self.ndim.

v_[crds_] == v[crds], when v_[crds_]=new_value is effective.

dim() int

Nd of the spinarray object, syntax sugar for len(spinarray.shape)

Usage:

Nd = spinarray.dim()

embed(v_: Tensor, *, out: Tensor | None = None) Tensor

Embed compact data into the spinarray.mask

Usage:

out = spinarray.embed(v_, *, out)

Inputs:
  • v_: (N, nM, …), must be contiguous.

Optionals:
  • out: (N, *Nd, …), in-place holder.

Outputs:
  • out: (N, *Nd, …).

extract(v: Tensor, *, out_: Tensor | None = None) Tensor

Extract data with the spinarray.mask, making it compact

Usage:

out_ = spinarray.extract(v, *, out_)

Inputs:
  • v: (N, *Nd, …).

Optionals:
  • out_: (N, nM, …), in-place holder, must be contiguous.

Outputs:
  • out_: (N, nM, …).

freeprec(dur: Tensor, *, doEmbed: bool = False, doRelax: bool = True, doUpdate: bool = False, Δf: Tensor | None = None, Δf_: Tensor | None = None) Tensor

Free precession of duration dur

Typical usage:

M = obj.freeprec(dur, doEmbed=True, doRelax, doUpdate, Δf) M_ = obj.applypulse(dur, doEmbed=False, doRelax, doUpdate, Δf_)

Inputs:
  • dur: ()(N ⊻ 1,), “Sec”, duration of free-precession.

Optionals:
  • doEmbed: [t/F], return M or M_

  • doRelax: [T/f], do relaxation during free precession.

  • doUpdate: [t/F], update self.M_

  • Δf``⊻ ``Δf_: (N ⊻ 1,*Nd ⊻ nM), “Hz”, off-resonance.

Outputs:
  • MM_: (N,*Nd ⊻ nM,xyz)

Note

When doUpdate == True and doEmbed == False, the output compact magnetization Tensor is a reference to self.M_, and needs caution when being accessed.

mask_(*, mask: Tensor) Tensor

Extract the compact region of an input external mask.

Usage:

mask_ = spinarray.mask_(mask)

Inputs:
  • mask: (1, *Nd).

Outputs:
  • mask_: (1, nM), mask_ can be used on compact attributes.

numel() int

Number of spins for the spinarray object, incompact.

Syntax sugar of spinarray.mask.numel(), effectively prod(spinarray.size()).

Usage:

res = spinarray.numel()

pulse2beff(pulse: Pulse, *, doEmbed: bool = False, loc: Tensor | None = None, loc_: Tensor | None = None, Δf: Tensor | None = None, Δf_: Tensor | None = None, b1Map: Tensor | None = None, b1Map_: Tensor | None = None) Tensor

Compute B-effective of pulse with the spinarray’s parameters

Typical usage:

beff = spinarray.pulse2beff(pulse, *, loc, doEmbed=True, Δf, ``\ ``b1Map) beff_ = spinarray.pulse2beff(pulse, *, loc_, doEmbed=False, ``\ ``Δf_, b1Map_)

Inputs:
  • pulse: mrphy.mobjs.Pulse.

  • locloc_: (N,*Nd ⊻ nM,xyz), “cm”, locations.

Optionals:
  • doEmbed: [t/F], return beff or beff_

  • ΔfΔf_: (N,*Nd ⊻ nM), “Hz”, off-resonance.

  • b1Mapb1Map_: (N,*Nd ⊻ nM,xy,(nCoils)), transmit sensitivity.

Outputs:
  • beffbeff_: (N,*Nd ⊻ nM,xyz,nT).

size() tuple

Size of the spinarray object.

Syntax sugar of spinarray.shape.

Usage:

sz = spinarray.size()

to(*, device: device = device(type='cpu'), dtype: dtype = torch.float32) SpinArray

Duplicate the object to the prescribed device with dtype

Usage:

new_spinarray = spinarray.to(*, device, dtype)

Inputs:
  • device: torch.device

  • dtype: torch.dtype

Outputs:
  • new_spinarray: mrphy.mobjs.SpinArray object

mrphy.mobjs.SpinCube

class mrphy.mobjs.SpinCube(shape: tuple, fov: Tensor, *, mask: Tensor | None = None, ofst: Tensor = tensor([[0., 0., 0.]]), Δf: Tensor | None = None, Δf_: Tensor | None = None, T1: Tensor | None = None, T1_: Tensor | None = None, T2: Tensor | None = None, T2_: Tensor | None = None, γ: Tensor | None = None, γ_: Tensor | None = None, M: Tensor | None = None, M_: Tensor | None = None, device: device = device(type='cpu'), dtype: dtype = torch.float32)

Bases: SpinArray

mrphy.mobjs.SpinCube object

Usage:

SpinCube(shape, fov, mask, *, ofst, Δf_, T1_, T2_, γ_, M_, device,`` dtype)`` SpinCube(shape, fov, mask, *, ofst, Δf, T1, T2, γ, M, device,‘’ dtype)``

Inputs:
  • shape: tuple, e.g., (N, nx, ny, nz).

  • fov: (N, xyz), “cm”, field of view.

Optionals:
  • mask: (1, *Nd), where does compact attributes locate in Nd.

  • ofst: (N, xyz), Tensor “cm”, fov offset from iso-center.

  • ΔfΔf_: (N, *Nd ⊻ nM), “Hz”, off-resonance map.

  • T1T1_: (N, *Nd ⊻ nM), “Sec”, T1 relaxation coeff.

  • T2T2_: (N, *Nd ⊻ nM), “Sec”, T2 relaxation coeff.

  • γγ_: (N, *Nd ⊻ nM), “Hz/Gauss”, gyro ratio.

  • MM_: (N, *Nd ⊻ nM, xyz), spins, equilibrium [0 0 1].

  • device: torch.device.

  • dtype: torch.dtype

Properties:
  • spinarray: SpinArray object.

  • Δf_: (N, nM), “Hz”, off-resonance map.

  • loc_: (N, nM, xyz), “cm”, location of spins.

  • fov: (N, xyz), “cm”, field of view.

  • ofst: (N, xyz), “cm”, fov offset from iso-center.

applypulse(pulse: Pulse, *, doEmbed: bool = False, doRelax: bool = True, doUpdate: bool = False, b1Map: Tensor | None = None, b1Map_: Tensor | None = None) Tensor

Apply a pulse to the spincube object

Usage:

M = spincube.applypulse(pulse, *, doEmbed=True, doRelax, b1Map) M_ = spincube.applypulse(pulse, *, doEmbed=False, doRelax,`` b1Map_)``

Inputs:
  • pulse: mobjs.Pulse object.

Optionals:
  • doEmbed: [t/F], return M or M_.

  • doRelax: [T/f], do relaxation during Bloch simulation.

  • b1Mapb1Map_: (N,*Nd ⊻ nM,xy,(nCoils)), transmit sensitivity.

Outputs:
  • MM_: (N,*Nd ⊻ nM,xyz).

asdict(*, toNumpy: bool = True, doEmbed: bool = True) dict

Convert mrphy.mobjs.SpinCube object to dict

Usage:

d = spincube.asdict(*, toNumpy, doEmbed)

Inputs:
  • toNumpy: [T/f], convert Tensor to Numpy arrays.

  • doEmbed: [T/f], embed compactly stored (nM) data to the mask (*Nd).

Outputs:
  • d: dict, dictionary with detached data identical to the object.

freeprec(dur: Tensor, *, doEmbed: bool = False, doRelax: bool = True, doUpdate: bool = False) Tensor

Free precession of duration dur

Typical usage:

M = obj.freeprec(dur, doEmbed=True, doRelax, doUpdate) M_ = obj.applypulse(dur, doEmbed=False, doRelax, doUpdate)

Inputs:
  • dur: ()(N ⊻ 1,), “Sec”, duration of free-precession.

Optionals:
  • doEmbed: [t/F], return M or M_

  • doRelax: [T/f], do relaxation during free precession.

  • doUpdate: [t/F], update self.M_

Outputs:
  • MM_: (N,*Nd ⊻ nM,xyz)

Note

When doUpdate == True and doEmbed == False, the output compact magnetization Tensor is a reference to self.M_, and needs caution when being accessed.

pulse2beff(pulse: Pulse, *, doEmbed: bool = False, b1Map: Tensor | None = None, b1Map_: Tensor | None = None) Tensor

Compute B-effective of pulse with the spincube’s parameters

Typical usage:

beff = spincube.pulse2beff(pulse, *, doEmbed=True, b1Map) beff_ = spincube.pulse2beff(pulse, *, doEmbed=False, b1Map_)

Inputs:
  • pulse: mrphy.mobjs.Pulse.

Optionals:
  • doEmbed: [t/F], return beff or beff_.

  • b1Mapb1Map_: (N,*Nd ⊻ nM,xy,(nCoils)), transmit sensitivity.

Outputs:
  • beffbeff_: (N,*Nd ⊻ nM,xyz,nT).

to(*, device: device = device(type='cpu'), dtype: dtype = torch.float32) SpinCube

Duplicate the object to the prescribed device with dtype

Usage:

new_spincube = spincube.to(*, device, dtype)

Inputs:
  • device: torch.device.

  • dtype: torch.dtype.

Outputs:
  • new_spincube: mrphy.mobjs.SpinCube object.

mrphy.mobjs.Examples

class mrphy.mobjs.Examples

Class for quickly creating exemplary instances to play around with.

static pulse() Pulse

Create a mrphy.mobjs.Pulse object.

static spinarray() SpinArray

Create a mrphy.mobjs.SpinArray object.

static spincube() SpinCube

Create a mrphy.mobjs.SpinCube object.