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”,xyfor 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:
devicedtypeis_cudashape:(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”,xyfor 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.
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.T1⊻T1_: (N, *Nd ⊻ nM), “Sec”, T1 relaxation coeff.T2⊻T2_: (N, *Nd ⊻ nM), “Sec”, T2 relaxation coeff.γ⊻γ_: (N, *Nd ⊻ nM), “Hz/Gauss”, gyro ratio.M⊻M_: (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
maskof an object, e.g.,spinarray.mask[0] = True.Do NOT proceed indexed/masked assignments over any non-compact attribute, e.g.,
spinarray.T1[0] = T1Gorspinarray.T1[mask] = T1G. The underlying compact attributes will NOT be updated, since they do not share memory. The only exception is whentorch.all(mask == True)and the underlying compact is contiguous, where the non-compact is just aview((N, *Nd, ...)). Checkoutcrds_()andmask_()for indexed/masked access to compacts.
Tip
maskis 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 ofmaskmay seemingly be of interest, havingtorch.all(mask == True)and postponing the variations to eventual losses evaluation can be a better design, which allows reuse ofM_, 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.loc⊻loc_: (N,*Nd ⊻ nM,xyz), “cm”, locations.
- Optionals:
doEmbed: [t/F], returnMorM_doRelax: [T/f], do relaxation during Bloch simulation.doUpdate: [t/F], updateself.M_Δf``⊻ ``Δf_: (N,*Nd ⊻ nM), “Hz”, off-resonance.b1Map⊻b1Map_: (N,*Nd ⊻ nM,xy,(nCoils)), transmit sensitivity.
- Outputs:
M⊻M_: (N,*Nd ⊻ nM,xyz)
Note
When
doUpdate == True and doEmbed == False, the output compact magnetization Tensor is a reference toself.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], convertTensorto 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
1on thespinarray.maskis 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], whenv_[crds_]=new_valueis 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], returnMorM_doRelax: [T/f], do relaxation during free precession.doUpdate: [t/F], updateself.M_Δf``⊻ ``Δf_: (N ⊻ 1,*Nd ⊻ nM), “Hz”, off-resonance.
- Outputs:
M⊻M_: (N,*Nd ⊻ nM,xyz)
Note
When
doUpdate == True and doEmbed == False, the output compact magnetization Tensor is a reference toself.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(), effectivelyprod(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
pulsewith 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.loc⊻loc_: (N,*Nd ⊻ nM,xyz), “cm”, locations.
- Optionals:
doEmbed: [t/F], returnbefforbeff_Δf⊻Δf_: (N,*Nd ⊻ nM), “Hz”, off-resonance.b1Map⊻b1Map_: (N,*Nd ⊻ nM,xy,(nCoils)), transmit sensitivity.
- Outputs:
beff⊻beff_: (N,*Nd ⊻ nM,xyz,nT).
- size() tuple
Size of the spinarray object.
Syntax sugar of
spinarray.shape.- Usage:
sz = spinarray.size()
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:
SpinArraymrphy.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.T1⊻T1_: (N, *Nd ⊻ nM), “Sec”, T1 relaxation coeff.T2⊻T2_: (N, *Nd ⊻ nM), “Sec”, T2 relaxation coeff.γ⊻γ_: (N, *Nd ⊻ nM), “Hz/Gauss”, gyro ratio.M⊻M_: (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], returnMorM_.doRelax: [T/f], do relaxation during Bloch simulation.b1Map⊻b1Map_: (N,*Nd ⊻ nM,xy,(nCoils)), transmit sensitivity.
- Outputs:
M⊻M_: (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], convertTensorto 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], returnMorM_doRelax: [T/f], do relaxation during free precession.doUpdate: [t/F], updateself.M_
- Outputs:
M⊻M_: (N,*Nd ⊻ nM,xyz)
Note
When
doUpdate == True and doEmbed == False, the output compact magnetization Tensor is a reference toself.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
pulsewith 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], returnbefforbeff_.b1Map⊻b1Map_: (N,*Nd ⊻ nM,xy,(nCoils)), transmit sensitivity.
- Outputs:
beff⊻beff_: (N,*Nd ⊻ nM,xyz,nT).