Exemple #1
0
 def __init__(self, system=Opts()):
     self.system = system
     # EventLibrary.data is a dict
     self.shape_library = EventLibrary()
     self.rf_library = EventLibrary()
     self.grad_library = EventLibrary()
     self.adc_library = EventLibrary()
     self.delay_library = EventLibrary()
     self.block_events = {}
     self.rf_raster_time = self.system.rf_raster_time
     self.grad_raster_time = self.system.grad_raster_time
Exemple #2
0
    def compute(self):
        if 'ComputeEvents' in self.widgetEvents(
        ) or 'input' in self.portEvents():
            out_dict = {}
            max_grad = self.getVal('Maximum Gradient')
            max_grad_unit = self.getVal('Maximum Gradient Unit')
            max_slew = self.getVal('Maximum Slew Rate')
            max_slew_unit = self.getVal('Maximum Slew Rate Unit')
            tr = self.getVal('Repetition Time (s)')
            te = self.getVal('Echo Time (s)')
            fov = self.getVal('Field of View')
            Nx = self.getVal('Nx')
            Ny = self.getVal('Ny')
            rise_time = self.getVal('Rise Time (s)')
            rf_dead_time = self.getVal('RF Dead Time (s)')
            adc_dead_time = self.getVal('ADC Dead Time (s)')
            rf_raster_time = self.getVal('RF Raster Time (s)')
            rf_raster_time = rf_raster_time if rf_raster_time != '' else 1e-6
            grad_raster_time = self.getVal('Gradient Raster Time (s)')
            grad_raster_time = grad_raster_time if grad_raster_time != '' else 10e-6

            kwargs_for_opts = {
                'grad_unit': max_grad_unit,
                'slew_unit': max_slew_unit
            }
            keys_for_opts = [
                'max_grad', 'max_slew', 'tr', 'te', 'fov', 'Nx', 'Ny',
                'rise_tme', 'rf_dead_time', 'adc_dead_time', 'rf_raster_time',
                'grad_raster_time'
            ]
            values_for_opts = [
                max_grad, max_slew, tr, te, fov, Nx, Ny, rise_time,
                rf_dead_time, adc_dead_time, rf_raster_time, grad_raster_time
            ]
            for i in range(len(values_for_opts)):
                kwargs_for_opts[keys_for_opts[i]] = float(values_for_opts[i])

            system = Opts(kwargs_for_opts)
            out_dict['system'] = system

            self.setData('output', out_dict)

            # To display the computed info in the TextBox
            self.setAttr('System limits', val=str(system))

            return 0
Exemple #3
0
def makeadc(kwargs):
    """
    Makes a Holder object for an ADC Event.

    Parameters
    ----------
    kwargs : dict
        Key value mappings of ADC Event parameters_params and values.

    Returns
    -------
    adc : Holder
        ADC Event.
    """

    num_samples = kwargs.get("num_samples", 0)
    system = kwargs.get("system", Opts())
    dwell = kwargs.get("dwell", 0)
    duration = kwargs.get("duration", 0)
    delay = kwargs.get("delay", 0)
    freq_offset = kwargs.get("freq_offset", 0)
    phase_offset = kwargs.get("phase_offset", 0)

    adc = Holder()
    adc.type = 'adc'
    adc.num_samples = num_samples
    adc.dwell = dwell
    adc.delay = delay
    adc.freq_offset = freq_offset
    adc.phase_offset = phase_offset
    adc.dead_time = system.adc_dead_time

    if (dwell == 0 and duration == 0) or (dwell > 0 and duration > 0):
        raise ValueError("Either dwell or duration must be defined, not both")

    if duration > 0:
        # getcontext().prec = 4
        adc.dwell = float(Decimal(duration) / Decimal(num_samples))
    adc.duration = dwell * num_samples if dwell > 0 else 0
    return adc
def makearbitrary_grad(kwargs):
    """
    Makes a Holder object for an arbitrary gradient Event.

    Parameters
    ----------
    kwargs : dict
        Key value mappings of RF Event parameters_params and values.

    Returns
    -------
    grad : Holder
        Trapezoidal gradient Event configured based on supplied kwargs.
    """

    channel = kwargs.get("channel", "z")
    system = kwargs.get("system", Opts())
    waveform = kwargs.get("waveform")
    max_grad = kwargs.get("max_grad") if kwargs.get("max_grad",
                                                    0) > 0 else system.max_grad
    max_slew = kwargs.get("max_slew ") if kwargs.get(
        "max_slew ", 0) > 0 else system.max_slew

    g = np.reshape(waveform, (1, -1))
    slew = (g[0][1:] - g[0][0:-1]) / system.grad_raster_time
    if max(abs(slew)) > max_slew:
        raise ValueError('Slew rate violation {:f}'.format(
            max(abs(slew)) / max_slew * 100))
    if max(abs(g[0])) > max_grad:
        raise ValueError('Gradient amplitude violation {:f}'.format(
            max(abs(g)) / max_grad * 100))
    grad = Holder()
    grad.type = 'grad'
    grad.channel = channel
    grad.waveform = g
    grad.t = np.arange(len(g[0])) * system.grad_raster_time
    grad.t = np.reshape(grad.t, (1, -1))
    return grad
Exemple #5
0
This is starter code to demonstrate a working example of the Gradient Recalled Echo as a pure Python implementation.
"""
from math import pi

import numpy as np

from pulseq.core.Sequence.sequence import Sequence
from pulseq.core.calc_duration import calc_duration
from pulseq.core.make_adc import makeadc
from pulseq.core.make_delay import make_delay
from pulseq.core.make_sinc import make_sinc_pulse
from pulseq.core.make_trap import make_trapezoid
from pulseq.core.opts import Opts

kwargs_for_opts = {"rf_ring_down_time": 30e-6, "rf_dead_time": 100e-6}
system = Opts(kwargs_for_opts)
seq = Sequence(system)

fov = 220e-3
Nx = 256
Ny = 256
slice_thickness = 5e-3

flip = 15 * pi / 180
kwargs_for_sinc = {
    "flip_angle": flip,
    "system": system,
    "duration": 4e-3,
    "slice_thickness": slice_thickness,
    "apodization": 0.5,
    "time_bw_product": 4
 def compute(self):
     if 'File location' in self.widgetEvents():
         file_location = self.getVal('File location')
         seq = Sequence(Opts())
         seq.read(file_location)
         self.setData('output', {"seq": seq})
Exemple #7
0
def make_arbitrary_rf(kwargs, nargout=1):
    """
    Makes a Holder object for an arbitrary RF pulse Event.

    Parameters
    ----------
    kwargs : dict
        Key value mappings of RF Event parameters_params and values.
    nargout: int
        Number of output arguments to be returned. Default is 1, only RF Event is returned. Passing any number greater
        than 1 will return the Gz Event along with the RF Event.

    Returns
    -------
    rf : Holder
        RF Event configured based on supplied kwargs.
    gz : Holder
        Slice select trapezoidal gradient Event.
    """

    flip_angle = kwargs.get("flip_angle")
    system = kwargs.get("system", Opts())
    signal = kwargs.get("signal", 0)
    freq_offset = kwargs.get("freq_offset", 0)
    phase_offset = kwargs.get("phase_offset", 0)
    time_bw_product = kwargs.get("time_bw_product", 0)
    bandwidth = kwargs.get("bandwidth", 0)
    max_grad = kwargs.get("max_grad", 0)
    max_slew = kwargs.get("max_slew", 0)
    slice_thickness = kwargs.get("slice_thickness", 0)

    signal = signal / sum(signal * system.rf_raster_time) * flip_angle / (2 * pi)
    N = len(signal)
    duration = N * system.rf_raster_time
    t = [x * system.rfRasterTime for x in range(0, N)]

    rf = Holder()
    rf.type = 'rf'
    rf.signal = signal
    rf.t = t
    rf.freq_offset = freq_offset
    rf.phase_offset = phase_offset
    rf.rf_dead_time = system.rf_dead_time
    rf.ring_down_time = system.rf_ring_down_time

    if nargout > 1:
        if slice_thickness <= 0:
            raise ValueError('Slice thickness must be provided')
        if bandwidth <= 0:
            raise ValueError('Bandwidth of pulse must be provided')

        system.maxgrad = max_grad if max_grad > 0 else system.maxgrad
        system.max_slew = max_slew if max_slew > 0 else system.max_slew

        BW = bandwidth
        if time_bw_product > 0:
            BW = time_bw_product / duration

        amplitude = BW / slice_thickness
        area = amplitude * duration
        kwargs_for_trap = {"channel": 'z', "system": system, "flat_time": duration, "flat_area": area}
        gz = make_trapezoid(**kwargs_for_trap)

        t_fill = np.arange(1, round(gz.rise_time / 1e-6) + 1) * 1e-6
        rf.t = [t_fill, rf.t + t_fill[-1], t_fill + rf.t[-1] + t_fill[-1]]
        rf.signal = [np.zeros(len(t_fill)), rf.signal, np.zeros(len(t_fill))]

    if rf.ring_down_time > 0:
        t_fill = np.arange(1, round(gz.rise_time / 1e-6) + 1) * 1e-6
        rf.t = [rf.t, rf.t[-1] + t_fill]
        rf.signal = [rf.signal, np.zeros(len(t_fill))]

    return rf, gz
def make_block_pulse(kwargs, nargout=1):
    """
    Makes a Holder object for an RF pulse Event.

    Parameters
    ----------
    kwargs : dict
        Key value mappings of RF Event parameters_params and values.
    nargout: int
        Number of output arguments to be returned. Default is 1, only RF Event is returned. Passing any number greater
        than 1 will return the Gz Event along with the RF Event.

    Returns
    -------
    Tuple consisting of:
    rf : Holder
        RF Event configured based on supplied kwargs.
    gz : Holder
        Slice select trapezoidal gradient Event.
    """

    flip_angle = kwargs.get("flip_angle")
    system = kwargs.get("system", Opts())
    duration = kwargs.get("duration", 0)
    freq_offset = kwargs.get("freq_offset", 0)
    phase_offset = kwargs.get("phase_offset", 0)
    time_bw_product = kwargs.get("time_bw_product", 4)
    bandwidth = kwargs.get("bandwidth", 0)
    max_grad = kwargs.get("max_grad", 0)
    max_slew = kwargs.get("max_slew", 0)
    slice_thickness = kwargs.get("slice_thickness", 0)

    if duration == 0:
        if time_bw_product > 0:
            duration = time_bw_product / bandwidth
        elif bandwidth > 0:
            duration = 1 / (4 * bandwidth)
        else:
            raise ValueError('Either bandwidth or duration must be defined')

    BW = 1 / (4 * duration)
    N = round(duration / 1e-6)
    t = [x * system.rf_raster_time for x in range(N)]
    signal = flip_angle / (2 * np.pi) / duration * np.ones(len(t))

    rf = Holder()
    rf.type = 'rf'
    rf.signal = signal
    rf.t = t
    rf.freq_offset = freq_offset
    rf.phase_offset = phase_offset
    rf.dead_time = system.rf_dead_time
    rf.ring_down_time = system.rf_ring_down_time

    fill_time = 0
    if nargout > 1:
        if slice_thickness < 0:
            raise ValueError('Slice thickness must be provided')

        if max_grad > 0:
            system.max_grad = max_grad
        if max_slew > 0:
            system.max_slew = max_slew

        amplitude = BW / slice_thickness
        area = amplitude * duration
        kwargs_for_trap = {
            'channel': 'z',
            'system': system,
            'flat_time': duration,
            'flat_area': area
        }
        gz = make_trapezoid(kwargs_for_trap)

        fill_time = gz.rise_time
        t_fill = np.array(
            [x * 1e-6 for x in range(int(round(fill_time / 1e-6)))])
        rf.t = np.array(
            [t_fill, rf.t + t_fill[-1], t_fill + rf.t[-1] + t_fill[-1]])
        rf.signal = np.array(
            [np.zeros(t_fill.size), rf.signal,
             np.zeros(t_fill.size)])

    if fill_time < rf.dead_time:
        fill_time = rf.dead_time - fill_time
        t_fill = np.array(
            [x * 1e-6 for x in range(int(round(fill_time / 1e-6)))])
        rf.t = np.insert(rf.t, 0, t_fill) + t_fill[-1]
        rf.t = np.reshape(rf.t, (1, len(rf.t)))
        rf.signal = np.insert(rf.signal, 0, np.zeros(t_fill.size))
        rf.signal = np.reshape(rf.signal, (1, len(rf.signal)))

    if rf.ring_down_time > 0:
        t_fill = np.arange(1, round(rf.ring_down_time / 1e-6) + 1) * 1e-6
        rf.t = [rf.t, rf.t[-1] + t_fill]
        rf.signal = [rf.signal, np.zeros(len(t_fill))]

    if nargout > 1:
        return rf, gz
    else:
        return rf
def make_trapezoid(kwargs):
    """
    Makes a Holder object for an trapezoidal gradient Event.

    Parameters
    ----------
    kwargs : dict
        Key value mappings of trapezoidal gradient Event parameters_params and values.

    Returns
    -------
    grad : Holder
        Trapezoidal gradient Event configured based on supplied kwargs.
    """

    channel = kwargs.get("channel", "z")
    system = kwargs.get("system", Opts())
    duration = kwargs.get("duration", 0)
    area_result = kwargs.get("area", -1)
    flat_time_result = kwargs.get("flat_time", 0)
    flat_area_result = kwargs.get("flat_area", -1)
    amplitude_result = kwargs.get("amplitude", -1)
    max_grad = kwargs.get("max_grad", 0)
    max_slew = kwargs.get("max_slew", 0)
    rise_time = kwargs.get("rise_time", 0)

    max_grad = max_grad if max_grad > 0 else system.max_grad
    max_slew = max_slew if max_slew > 0 else system.max_slew
    rise_time = rise_time if rise_time > 0 else system.rise_time

    if area_result == -1 and flat_area_result == -1 and amplitude_result == -1:
        raise ValueError('Must supply either '
                         'area'
                         ', '
                         'flat_area'
                         ' or '
                         'amplitude'
                         '')

    if flat_time_result > 0:
        amplitude = amplitude_result if (amplitude_result != -1) else (
            flat_area_result / flat_time_result)
        if rise_time == 0:
            rise_time = abs(amplitude) / max_slew
            rise_time = ceil(
                rise_time / system.grad_raster_time) * system.grad_raster_time
        fall_time, flat_time = rise_time, flat_time_result
    elif duration > 0:
        if amplitude_result != -1:
            amplitude = amplitude_result
        else:
            if rise_time == 0:
                dC = 1 / abs(2 * max_slew) + 1 / abs(2 * max_slew)
                amplitude = (duration - sqrt(
                    pow(duration, 2) - 4 * abs(area_result) * dC)) / (2 * dC)
            else:
                amplitude = area_result / (duration - rise_time)

        if rise_time == 0:
            rise_time = ceil(amplitude / max_slew /
                             system.grad_raster_time) * system.grad_raster_time

        fall_time = rise_time
        flat_time = (duration - rise_time - fall_time)

        amplitude = area_result / (rise_time / 2 + fall_time / 2 + flat_time
                                   ) if amplitude_result == -1 else amplitude
    else:
        raise ValueError('Must supply a duration')

    if abs(amplitude) > max_grad:
        raise ValueError("Amplitude violation")

    grad = Holder()
    grad.type = "trap"
    grad.channel = channel
    grad.amplitude = amplitude
    grad.rise_time = rise_time
    grad.flat_time = flat_time
    grad.fall_time = fall_time
    grad.area = amplitude * (flat_time + rise_time / 2 + fall_time / 2)
    grad.flat_area = amplitude * flat_time

    return grad
    def make_se_epi(self):
        kwargs_for_opts = {
            "max_grad": self.max_grad,
            "grad_unit": "mT/m",
            "max_slew": self.max_slew,
            "slew_unit": "T/m/s",
            "rf_dead_time": 10e-6,
            "adc_dead_time": 10e-6
        }
        system = Opts(kwargs_for_opts)
        seq = Sequence(system)

        slice_thickness = 3e-3

        flip = 90 * pi / 180
        kwargs_for_sinc = {
            "flip_angle": flip,
            "system": system,
            "duration": 2.5e-3,
            "slice_thickness": slice_thickness,
            "apodization": 0.5,
            "time_bw_product": 4
        }
        rf, gz = make_sinc_pulse(kwargs_for_sinc, 2)
        # plt.plot(rf.t[0], rf.signal[0])
        # plt.show()

        delta_k = 1 / self.fov
        k_width = self.Nx * delta_k
        readout_time = self.Nx * 4e-6
        kwargs_for_gx = {
            "channel": 'x',
            "system": system,
            "flat_area": k_width,
            "flat_time": readout_time
        }
        gx = make_trapezoid(kwargs_for_gx)
        kwargs_for_adc = {
            "num_samples": self.Nx,
            "system": system,
            "duration": gx.flat_time,
            "delay": gx.rise_time
        }
        adc = makeadc(kwargs_for_adc)

        pre_time = 8e-4
        kwargs_for_gxpre = {
            "channel": 'x',
            "system": system,
            "area": -gx.area / 2,
            "duration": pre_time
        }
        gx_pre = make_trapezoid(kwargs_for_gxpre)
        kwargs_for_gz_reph = {
            "channel": 'z',
            "system": system,
            "area": -gz.area / 2,
            "duration": pre_time
        }
        gz_reph = make_trapezoid(kwargs_for_gz_reph)
        kwargs_for_gy_pre = {
            "channel": 'y',
            "system": system,
            "area": -self.Ny / 2 * delta_k,
            "duration": pre_time
        }
        gy_pre = make_trapezoid(kwargs_for_gy_pre)

        dur = ceil(2 * sqrt(delta_k / system.max_slew) / 10e-6) * 10e-6
        kwargs_for_gy = {
            "channel": 'y',
            "system": system,
            "area": delta_k,
            "duration": dur
        }
        gy = make_trapezoid(kwargs_for_gy)

        flip = 180 * pi / 180
        kwargs_for_sinc = {
            "flip_angle": flip,
            "system": system,
            "duration": 2.5e-3
        }
        rf180 = make_block_pulse(kwargs_for_sinc)
        kwargs_for_gz_spoil = {
            "channel": 'z',
            "system": system,
            "area": gz.area * 2,
            "duration": 3 * pre_time
        }
        gz_spoil = make_trapezoid(kwargs_for_gz_spoil)

        TE = self.te
        duration_to_center = (self.Nx / 2 + 0.5) * calc_duration(
            gx) + self.Ny / 2 * calc_duration(gy)
        delayTE1 = TE / 2 - calc_duration(gz) / 2 - pre_time - calc_duration(
            gz_spoil) - calc_duration(rf180) / 2
        delayTE2 = TE / 2 - calc_duration(rf180) / 2 - calc_duration(
            gz_spoil) - duration_to_center
        delay1 = make_delay(delayTE1)
        delay2 = make_delay(delayTE2)

        seq.add_block(rf, gz)
        seq.add_block(gx_pre, gy_pre, gz_reph)
        seq.add_block(delay1)
        seq.add_block(gz_spoil)
        seq.add_block(rf180)
        seq.add_block(gz_spoil)
        seq.add_block(delay2)
        for i in range(self.Ny):
            seq.add_block(gx, adc)
            seq.add_block(gy)
            gx.amplitude = -gx.amplitude
        seq.add_block(make_delay(1))

        return seq
Exemple #11
0
def make_sinc_pulse(kwargs, nargout=1):
    """
    Makes a Holder object for an RF pulse Event.

    Parameters
    ----------
    kwargs : dict
        Key value mappings of RF Event parameters_params and values.
    nargout: int
        Number of output arguments to be returned. Default is 1, only RF Event is returned. Passing any number greater
        than 1 will return the Gz Event along with the RF Event.

    Returns
    -------
    rf : Holder
        RF Event configured based on supplied kwargs.
    gz : Holder
        Slice select trapezoidal gradient Event.
    """

    flip_angle = kwargs.get("flip_angle")
    system = kwargs.get("system", Opts())
    duration = kwargs.get("duration", 0)
    freq_offset = kwargs.get("freq_offset", 0)
    phase_offset = kwargs.get("phase_offset", 0)
    time_bw_product = kwargs.get("time_bw_product", 4)
    apodization = kwargs.get("apodization", 0)
    max_grad = kwargs.get("max_grad", 0)
    max_slew = kwargs.get("max_slew", 0)
    slice_thickness = kwargs.get("slice_thickness", 0)

    BW = time_bw_product / duration
    alpha = apodization
    N = int(round(duration / 1e-6))
    t = np.zeros((1, N))
    for x in range(1, N + 1):
        t[0][x - 1] = x * system.rf_raster_time
    tt = t - (duration / 2)
    window = np.zeros((1, tt.shape[1]))
    for x in range(0, tt.shape[1]):
        window[0][x] = 1.0 - alpha + alpha * np.cos(2 * np.pi * tt[0][x] / duration)
    signal = np.multiply(window, np.sinc(BW * tt))
    flip = np.sum(signal) * system.rf_raster_time * 2 * np.pi
    signal = signal * flip_angle / flip

    rf = Holder()
    rf.type = 'rf'
    rf.signal = signal
    rf.t = t
    rf.freq_offset = freq_offset
    rf.phase_offset = phase_offset
    rf.dead_time = system.rf_dead_time
    rf.ring_down_time = system.rf_ring_down_time

    fill_time = 0
    if nargout > 1:
        if slice_thickness == 0:
            raise ValueError('Slice thickness must be provided')

        system.max_grad = max_grad if max_grad > 0 else system.max_grad
        system.max_slew = max_slew if max_slew > 0 else system.max_slew

        amplitude = BW / slice_thickness
        area = amplitude * duration
        kwargs_for_trap = {"channel": 'z', "system": system, "flat_time": duration, "flat_area": area}
        gz = make_trapezoid(kwargs_for_trap)

        fill_time = gz.rise_time
        nfill_time = int(round(fill_time / 1e-6))
        t_fill = np.zeros((1, nfill_time))
        for x in range(1, nfill_time + 1):
            t_fill[0][x - 1] = x * 1e-6
        temp = np.concatenate((t_fill[0], rf.t[0] + t_fill[0][-1]))
        temp = temp.reshape((1, len(temp)))
        rf.t = np.resize(rf.t, temp.shape)
        rf.t[0] = temp
        z = np.zeros((1, t_fill.shape[1]))
        temp2 = np.concatenate((z[0], rf.signal[0]))
        temp2 = temp2.reshape((1, len(temp2)))
        rf.signal = np.resize(rf.signal, temp2.shape)
        rf.signal[0] = temp2

    # Add dead time to start of pulse, if required
    if fill_time < rf.dead_time:
        fill_time = rf.dead_time - fill_time
        t_fill = (np.arange(int(round(fill_time / 1e-6))) * 1e-6)[np.newaxis, :]
        rf.t = np.concatenate((t_fill, (rf.t + t_fill[0, -1])), axis=1)
        rf.signal = np.concatenate((np.zeros(t_fill.shape), rf.signal), axis=1)

    if rf.ring_down_time > 0:
        t_fill = (np.arange(1, round(rf.ring_down_time / 1e-6) + 1) * 1e-6)[np.newaxis, :]
        rf.t = np.concatenate((rf.t, rf.t[0, -1] + t_fill), axis=1)
        rf.signal = np.concatenate((rf.signal, np.zeros(t_fill.shape)), axis=1)

    # Following 2 lines of code are workarounds for numpy returning 3.14... for np.angle(-0.00...)
    negative_zero_indices = np.where(rf.signal == -0.0)
    rf.signal[negative_zero_indices] = 0

    if nargout > 1:
        return rf, gz
    else:
        return rf