Example #1
0
    def __init__(self,
                 grad_unit: str = None,
                 slew_unit: str = None,
                 max_grad: float = None,
                 max_slew: float = None,
                 rise_time: float = None,
                 rf_dead_time: float = 0,
                 rf_ringdown_time: float = 0,
                 adc_dead_time: float = 0,
                 rf_raster_time: float = 1e-6,
                 grad_raster_time: float = 10e-6,
                 gamma: float = 42.576e6):
        valid_grad_units = ['Hz/m', 'mT/m', 'rad/ms/mm']
        valid_slew_units = ['Hz/m/s', 'mT/m/ms', 'T/m/s', 'rad/ms/mm/ms']

        if grad_unit is not None and grad_unit not in valid_grad_units:
            raise ValueError(
                f'Invalid gradient unit, must be one of Hz/m, mT/m or rad/ms/mm. You passed: {grad_unit}'
            )

        if slew_unit is not None and slew_unit not in valid_slew_units:
            raise ValueError(
                f'Invalid slew rate unit, must be one of Hz/m/s, mT/m/ms, T/m/s or rad/ms/mm/ms. You '
                f'passed: {slew_unit}')

        if max_grad is None:
            max_grad = convert(from_value=40, from_unit='mT/m', gamma=gamma)
        else:
            max_grad = convert(from_value=max_grad,
                               from_unit=grad_unit,
                               to_unit='Hz/m',
                               gamma=gamma)

        if max_slew is None:
            max_slew = convert(from_value=170, from_unit='T/m/s', gamma=gamma)
        else:
            max_slew = convert(from_value=max_slew,
                               from_unit=slew_unit,
                               to_unit='Hz/m',
                               gamma=gamma)

        if rise_time is not None:
            max_slew = None

        self.max_grad = max_grad
        self.max_slew = max_slew
        self.rise_time = rise_time
        self.rf_dead_time = rf_dead_time
        self.rf_ringdown_time = rf_ringdown_time
        self.adc_dead_time = adc_dead_time
        self.rf_raster_time = rf_raster_time
        self.grad_raster_time = grad_raster_time
        self.gamma = gamma
Example #2
0
    def __init__(self,
                 adc_dead_time: float = 0,
                 gamma: float = 42.576e6,
                 grad_raster_time: float = 10e-6,
                 grad_unit: str = 'Hz/m',
                 max_grad: float = 0,
                 max_slew: float = 0,
                 rf_dead_time: float = 0,
                 rf_raster_time: float = 1e-6,
                 rf_ringdown_time: float = 0,
                 rise_time: float = 0,
                 slew_unit: str = 'Hz/m/s'):
        valid_grad_units = ['Hz/m', 'mT/m', 'rad/ms/mm']
        valid_slew_units = ['Hz/m/s', 'mT/m/ms', 'T/m/s', 'rad/ms/mm/ms']

        if grad_unit not in valid_grad_units:
            raise ValueError(
                f"Invalid gradient unit. Must be one of 'Hz/m', 'mT/m' or 'rad/ms/mm'. "
                f"Passed: {grad_unit}")

        if slew_unit not in valid_slew_units:
            raise ValueError(
                f"Invalid slew rate unit. Must be one of 'Hz/m/s', 'mT/m/ms', 'T/m/s' or 'rad/ms/mm/ms'. "
                f"Passed: {slew_unit}")

        if max_grad == 0:
            max_grad = convert(from_value=40, from_unit='mT/m', gamma=gamma)
        else:
            max_grad = convert(from_value=max_grad,
                               from_unit=grad_unit,
                               to_unit='Hz/m',
                               gamma=gamma)

        if max_slew == 0:
            max_slew = convert(from_value=170, from_unit='T/m/s', gamma=gamma)
        else:
            max_slew = convert(from_value=max_slew,
                               from_unit=slew_unit,
                               to_unit='Hz/m',
                               gamma=gamma)

        if rise_time != 0:
            max_slew = 0

        self.max_grad = max_grad
        self.max_slew = max_slew
        self.rise_time = rise_time
        self.rf_dead_time = rf_dead_time
        self.rf_ringdown_time = rf_ringdown_time
        self.adc_dead_time = adc_dead_time
        self.rf_raster_time = rf_raster_time
        self.grad_raster_time = grad_raster_time
        self.gamma = gamma
Example #3
0
    def __init__(self,
                 grad_unit=None,
                 slew_unit=None,
                 max_grad=None,
                 max_slew=None,
                 rise_time=None,
                 rf_dead_time=0,
                 rf_ringdown_time=0,
                 adc_dead_time=0,
                 rf_raster_time=1e-6,
                 grad_raster_time=10e-6,
                 gamma=42.576e6):
        valid_grad_units = ['Hz/m', 'mT/m', 'rad/ms/mm']
        valid_slew_units = ['Hz/m/s', 'mT/m/ms', 'T/m/s', 'rad/ms/mm/ms']

        if grad_unit is not None and grad_unit not in valid_grad_units:
            raise ValueError()

        if slew_unit is not None and slew_unit not in valid_slew_units:
            raise ValueError()

        if max_grad is None:
            max_grad = convert(from_value=40, from_unit='mT/m', gamma=gamma)
        else:
            max_grad = convert(from_value=max_grad,
                               from_unit=grad_unit,
                               to_unit='Hz/m',
                               gamma=gamma)

        if max_slew is None:
            max_slew = convert(from_value=170, from_unit='T/m/s', gamma=gamma)
        else:
            max_slew = convert(from_value=max_slew,
                               from_unit=slew_unit,
                               to_unit='Hz/m',
                               gamma=gamma)

        if rise_time is not None:
            max_slew = None

        self.max_grad = max_grad
        self.max_slew = max_slew
        self.rise_time = rise_time
        self.rf_dead_time = rf_dead_time
        self.rf_ringdown_time = rf_ringdown_time
        self.adc_dead_time = adc_dead_time
        self.rf_raster_time = rf_raster_time
        self.grad_raster_time = grad_raster_time
        self.gamma = gamma
Example #4
0
def test_report(self):
    """
    Analyze the sequence and return a text report.
    """
    # Find RF pulses and list flip angles
    flip_angles_deg = []
    for k in self.rf_library.keys:
        lib_data = self.rf_library.data[k]
        rf = self.rf_from_lib_data(lib_data)
        flip_angles_deg.append(np.abs(np.sum(rf.signal) * rf.t[0] * 360))

    flip_angles_deg = np.unique(flip_angles_deg)

    # Calculate TE, TR
    duration, num_blocks, event_count = self.duration()

    k_traj_adc, k_traj, t_excitation, t_refocusing, t_adc = self.calculate_kspace(
    )

    k_abs_adc = np.sqrt(np.sum(np.square(k_traj_adc), axis=0))
    k_abs_echo, index_echo = np.min(k_abs_adc), np.argmin(k_abs_adc)
    t_echo = t_adc[index_echo]
    t_ex_tmp = t_excitation[t_excitation < t_echo]
    TE = t_echo - t_ex_tmp[-1]

    if len(t_excitation) < 2:
        TR = duration
    else:
        t_ex_tmp1 = t_excitation[t_excitation > t_echo]
        if len(t_ex_tmp1) == 0:
            TR = t_ex_tmp[-1] - t_ex_tmp[-2]
        else:
            TR = t_ex_tmp1[0] - t_ex_tmp[-1]

    # Check sequence dimensionality and spatial resolution
    k_extent = np.max(np.abs(k_traj_adc), axis=1)
    k_scale = np.max(k_extent)
    if k_scale != 0:
        k_bins = 4e6
        k_threshold = k_scale / k_bins

        # Detect unused dimensions and delete them
        if np.any(k_extent < k_threshold):
            k_traj_adc = np.delete(k_traj_adc,
                                   np.where(k_extent < k_threshold),
                                   axis=0)
            k_extent = np.delete(k_extent,
                                 np.where(k_extent < k_threshold),
                                 axis=0)

        # Bin the k-space trajectory to detect repetitions / slices
        k_len = k_traj_adc.shape[1]
        k_repeat = np.zeros(k_len)

        k_map = dict()
        for i in range(k_len):
            l = k_bins + np.round(k_traj_adc[:, i] / k_threshold)
            key_string = ('{:.0f} ' * len(l)).format(*l)
            if key_string in k_map:
                k_repeat[i] = k_map[key_string] + 1
            else:
                k_repeat[i] = 1
            k_map[key_string] = k_repeat[i]

        repeats = np.max(k_repeat)

        k_traj_rep1 = k_traj_adc[:, k_repeat == 1]

        k_counters = np.zeros(k_traj_rep1.shape)
        dims = k_traj_rep1.shape[0]
        ordering = dict()
        for j in range(dims):
            c = 1
            k_map = dict()
            for i in range(k_traj_rep1.shape[1]):
                key = round(k_traj_rep1[j, i] / k_threshold)
                if key in k_map:
                    k_counters[j, i] = k_map[key]
                else:
                    k_counters[j, i] = c
                    k_map[key] = c
                    c += 1
            ordering[j] = k_map.values()

        unique_k_positions = np.max(k_counters, axis=1)
        is_cartesian = np.prod(unique_k_positions) == k_traj_rep1.shape[1]
    else:
        unique_k_positions = 1

    gw = self.gradient_waveforms()
    gws = (gw[:, 1:] - gw[:, :-1]) / self.system.grad_raster_time
    ga = np.max(np.abs(gw), axis=1)
    gs = np.max(np.abs(gws), axis=1)

    ga_abs = np.max(np.sqrt(np.sum(np.square(gw), axis=0)))
    gs_abs = np.max(np.sqrt(np.sum(np.square(gws), axis=0)))

    timing_ok, timing_error_report = self.check_timing()

    report = f'Number of blocks: {num_blocks}\n' \
        f'Number of events:\n' \
        f'RF: {event_count[1]:6.0f}\n' \
        f'Gx: {event_count[2]:6.0f}\n' \
        f'Gy: {event_count[3]:6.0f}\n' \
        f'Gz: {event_count[4]:6.0f}\n' \
        f'ADC: {event_count[5]:6.0f}\n' \
        f'Delay: {event_count[0]:6.0f}\n' \
        f'Sequence duration: {duration:.6f} s\n' \
        f'TE: {TE:.6f} s\n' \
        f'TR: {TR:.6f} s\n'
    report += 'Flip angle: ' + ('{:.02f} ' * len(flip_angles_deg)).format(
        *flip_angles_deg) + 'deg\n'
    report += 'Unique k-space positions (aka cols, rows, etc.): ' + (
        '{:.0f} ' * len(unique_k_positions)).format(*unique_k_positions) + '\n'

    if len(np.where(unique_k_positions > 1)):
        report += f'Dimensions: {len(k_extent)}\n'
        report += ('Spatial resolution: {:.02f} mm\n' *
                   len(k_extent)).format(*(0.5 / k_extent * 1e3))
        report += f'Repetitions/slices/contrasts: {repeats}\n'

        if is_cartesian:
            report += 'Cartesian encoding trajectory detected\n'
        else:
            report += 'Non-cartesian/irregular encoding trajectory detected (eg: EPI, spiral, radial, etc.)\n'

    if timing_ok:
        report += 'Block timing check passed successfully\n'
    else:
        report += f'Block timing check failed. Error listing follows:\n {timing_error_report}'

    ga_converted = convert(from_value=ga, from_unit='Hz/m', to_unit='mT/m')
    gs_converted = convert(from_value=gs, from_unit='Hz/m/s', to_unit='T/m/s')
    report += 'Max gradient: ' + ('{:.0f} ' * len(ga)).format(
        *ga) + 'Hz/m == ' + ('{:.02f} ' * len(ga_converted)).format(
            *ga_converted) + 'mT/m\n'
    report += 'Max slew rate: ' + ('{:.0f} ' * len(gs)).format(
        *gs) + 'Hz/m/s == ' + ('{:.02f} ' * len(gs_converted)).format(
            *gs_converted) + 'mT/m/s\n'

    ga_abs_converted = convert(from_value=ga_abs,
                               from_unit='Hz/m',
                               to_unit='mT/m')
    gs_abs_converted = convert(from_value=gs_abs,
                               from_unit='Hz/m/s',
                               to_unit='T/m/s')
    report += f'Max absolute gradient: {ga_abs:.0f} Hz/m == {ga_abs_converted:.2f} mT/m\n'
    report += f'Max absolute slew rate: {gs_abs:g} Hz/m/s == {gs_abs_converted:.2f} T/m/s'

    return report