Esempio n. 1
0
def compress_shape(decompressed_shape):
    """
    Returns a run-length encoded compressed shape.

    Parameters
    ----------
    decompressed_shape : ndarray
        Decompressed shape.

    Returns
    -------
    compressed_shape : Holder
        A Holder object containing the shape of the compressed shape ndarray and the compressed shape ndarray itself.
    """

    if decompressed_shape.shape[0] != 1:
        raise ValueError("input should be of shape (1,x)")
    if not isinstance(decompressed_shape, np.ndarray):
        raise TypeError("input should be of type numpy.ndarray")

    data = np.array([decompressed_shape[0][0]])
    data = np.concatenate((data, np.diff(decompressed_shape[0])))

    mask_changes = np.array([1])
    diff_as_ones = (abs(np.diff(data)) > 1e-8).astype(int)
    mask_changes = np.concatenate((mask_changes, diff_as_ones))
    vals = data[np.nonzero(mask_changes)].astype(float)
    k = np.array(np.nonzero(np.append(mask_changes, 1)))
    k = k.reshape((1, k.shape[1]))
    n = np.diff(k)[0]

    n_extra = (n - 2).astype(float)
    vals2 = np.copy(vals)
    vals2[np.where(n_extra < 0)] = np.NAN
    n_extra[np.where(n_extra < 0)] = np.NAN
    v = np.array([vals, vals2, n_extra])
    v = np.concatenate(np.hsplit(v, v.shape[1]))
    finite_vals = np.isfinite(v)
    v = v[finite_vals]
    v_abs = abs(v)
    smallest_indices = np.where(v_abs < 1e-10)
    v[smallest_indices] = 0

    compressed_shape = Holder()
    compressed_shape.num_samples = decompressed_shape.shape[1]
    compressed_shape.data = v.reshape([1, v.shape[0]])
    return compressed_shape
Esempio n. 2
0
def makedelay(d):
    """
    Makes a Holder object for an delay Event.

    Parameters
    ----------
    d : float
        Delay time, in seconds.

    Returns
    -------
    delay : Holder
        Delay Event.
    """

    delay = Holder()
    if d < 0:
        raise ValueError('Delay {:.2f} ms is invalid'.format(d * 1e3))
    delay.type = 'delay'
    delay.delay = d
    return delay
Esempio n. 3
0
def makearbitrarygrad(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_result = kwargs.get("max_grad", 0)
    max_slew_result = kwargs.get("max_slew", 0)

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

    g = waveform
    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.array([[x * system.grad_raster_time
                        for x in range(len(g[0]))]])
    return grad
Esempio n. 4
0
def get_block(self, block_index):
    """
    Returns Block at position specified by block_index.

    Parameters
    ----------
    block_index : int
        Index of Block to be retrieved.

    Returns
    -------
    block : dict
        Block at position specified by block_index.
    """

    block = {}
    event_ind = self.block_events[block_index]
    if event_ind[0] > 0:
        delay = Holder()
        delay.type = 'delay'
        delay.delay = self.delay_library.data[event_ind[0]]
        block['delay'] = delay
    elif event_ind[1] > 0:
        rf = Holder()
        rf.type = 'rf'
        lib_data = self.rf_library.data[event_ind[1]]

        amplitude, mag_shape, phase_shape = lib_data[0], lib_data[1], lib_data[
            2]
        shape_data = self.shape_library.data[mag_shape]
        compressed = Holder()
        compressed.num_samples = shape_data[0][0]
        compressed.data = shape_data[0][1:]
        compressed.data = compressed.data.reshape(
            (1, compressed.data.shape[0]))
        mag = decompress_shape(compressed)
        shape_data = self.shape_library.data[phase_shape]
        compressed.num_samples = shape_data[0][0]
        compressed.data = shape_data[0][1:]
        compressed.data = compressed.data.reshape(
            (1, compressed.data.shape[0]))
        phase = decompress_shape(compressed)
        rf.signal = 1j * 2 * np.pi * phase
        rf.signal = amplitude * mag * np.exp(rf.signal)
        rf.t = [(x * self.rf_raster_time)
                for x in range(1,
                               max(mag.shape) + 1)]
        rf.t = np.reshape(rf.t, (1, len(rf.t)))
        rf.freq_offset = lib_data[3]
        rf.phase_offset = lib_data[4]
        if max(lib_data.shape) < 6:
            lib_data = np.append(lib_data, 0)
        rf.dead_time = lib_data[5]

        block['rf'] = rf
    grad_channels = ['gx', 'gy', 'gz']
    for i in range(1, len(grad_channels) + 1):
        if event_ind[2 + (i - 1)] > 0:
            grad, compressed = Holder(), Holder()
            type = self.grad_library.type[event_ind[2 + (i - 1)]]
            lib_data = self.grad_library.data[event_ind[2 + (i - 1)]]
            grad.type = 'trap' if type == 't' else 'grad'
            grad.channel = grad_channels[i - 1][1]
            if grad.type == 'grad':
                amplitude = lib_data[0]
                shape_id = lib_data[1]
                shape_data = self.shape_library.data[shape_id]
                compressed.num_samples = shape_data[0][0]
                compressed.data = np.array([shape_data[0][1:]])
                g = decompress_shape(compressed)
                grad.waveform = amplitude * g
                grad.t = np.array([[
                    x * self.grad_raster_time for x in range(1, g.size + 1)
                ]])
            else:
                grad.amplitude, grad.rise_time, grad.flat_time, grad.fall_time = [
                    lib_data[x] for x in range(4)
                ]
                grad.area = grad.amplitude * (
                    grad.flat_time + grad.rise_time / 2 + grad.fall_time / 2)
                grad.flat_area = grad.amplitude * grad.flat_time
            block[grad_channels[i - 1]] = grad

    if event_ind[5] > 0:
        lib_data = self.adc_library.data[event_ind[5]]
        if max(lib_data.shape) < 6:
            lib_data = np.append(lib_data, 0)
        adc = Holder()
        adc.num_samples, adc.dwell, adc.delay, adc.freq_offset, adc.phase_offset, adc.dead_time = [
            lib_data[x] for x in range(6)
        ]
        adc.type = 'adc'
        block['adc'] = adc
    return block
Esempio n. 5
0
def makeblockpulse(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

    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 = maketrapezoid(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 nargout > 1:
        return rf, gz
    else:
        return rf
Esempio n. 6
0
def makearbitraryrf(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

    if nargout > 1:
        if slice_thickness <= 0:
            raise ValueError('Slice thickness must be provided')
        if bandwidth <= 0:
            raise ValueError('Bandwdith 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 = maketrapezoid(**kwargs_for_trap)

        t_fill = [x * 1e-6 for x in range(1, round(gz.riseTime / 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))]

    return rf, gz
Esempio n. 7
0
def makesincpulse(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)
    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

    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 = maketrapezoid(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

    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)))

    # 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
Esempio n. 8
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
Esempio n. 9
0
def maketrapezoid(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