Source code for pypulseqpp.sequences.preparation.inversion

"""Inversion-recovery preparation."""

from __future__ import annotations

__all__ = ["InversionPreparation"]

import pypulseqpp as pp

from ..excitation._base import RfModule, rf_reference

_AXES = ("x", "y", "z")


[docs] class InversionPreparation(RfModule): """Non-selective adiabatic inversion, followed by an optional crusher. The acquisition loop supplies the inversion-time delay. Parameters ---------- system : pypulseqpp.Opts System limits. duration_s : float, default=0.01 Inversion pulse duration (s). Shorter durations increase the required sweep rate and may violate the adiabatic condition. spoiling_cycles : float, default=4.0 Dephasing of the crusher, in cycles across ``voxel_size_m``. Zero omits the crusher. voxel_size_m : float, default=0.001 Length the dephasing is counted over (m) — the smallest voxel dimension, since that is the one that has to be spoiled. axis : {'z', 'x', 'y'}, default='z' Crusher axis. pulse_type : str, default='hypsec' Adiabatic sweep family. bandwidth_hz : float, default=40000.0 Frequency width of the sweep (Hz). adiabaticity : int, default=4 Sweep-rate margin over the adiabatic condition. labels : sequence of str, default=None Counters emitted on the inversion block, which starts each shot. The module creates the label events; the acquisition loop assigns their per-shot values. Attributes ---------- rf_prep : RfEvent The inversion pulse. gz_spoil : GradEvent The crusher, when ``spoiling_cycles`` is nonzero. prep_labels : LabelSetEvent or list of LabelSetEvent One per name in ``labels``, in order. Absent when ``labels`` is empty, and a bare event rather than a list when there is one. Raises ------ ValueError If ``voxel_size_m`` is not positive, ``spoiling_cycles`` is negative, or ``axis`` is not a gradient channel. Examples -------- >>> import pypulseqpp.sequences as design >>> import pypulseqpp as pp >>> prep = design.InversionPreparation(pp.Opts(), duration_s=8e-3) >>> len(prep.blocks) 2 >>> len(design.InversionPreparation(pp.Opts(), spoiling_cycles=0).blocks) 1 .. plot:: :include-source: false import matplotlib.pyplot as plt import pypulseqpp as pp from pypulseqpp.plot import plot_rf from pypulseqpp.sequences import InversionPreparation module = InversionPreparation(pp.Opts(), duration_s=8e-3) module.seq.paper_plot() plot_rf(module, plot_now=False) plt.show() """
[docs] def init_module( self, system: pp.Opts, duration_s: float = 10e-3, *, spoiling_cycles: float = 4.0, voxel_size_m: float = 1e-3, axis: str = "z", pulse_type: str = "hypsec", bandwidth_hz: float = 40e3, adiabaticity: int = 4, labels: tuple[str, ...] | None = None, ) -> None: if voxel_size_m <= 0: raise ValueError("voxel_size_m must be positive") if spoiling_cycles < 0: raise ValueError("spoiling_cycles must be >= 0") if axis not in _AXES: raise ValueError(f"axis must be one of {_AXES}, got {axis!r}") rf_prep = pp.make_adiabatic_pulse( pulse_type=pulse_type, duration=duration_s, bandwidth=bandwidth_hz, adiabaticity=adiabaticity, use="inversion", system=system, ) # A slot per label, so the loop has somewhere to put one. An iteration # can leave it out or say something different with it, but it cannot # add a slot the module never built. prep_labels = [ pp.make_label(type="SET", label=name, value=0) for name in labels or () ] self.seq = pp.Sequence(system) self.seq.add_block(rf_prep, *prep_labels) if spoiling_cycles: gz_spoil, _, _ = pp.make_crusher( spoiling_cycles, voxel_size_m, axis, system=system ) self.seq.add_block(gz_spoil) self.center = rf_reference(rf_prep)