Beispiel #1
0
def extract_chopped_pulses(AM, FM, Tp, N_seg, T_seg, fm_type):
    """

    """
    # Return a list of Pulseq RF objects
    list_RFs = []
    t = np.linspace(0, Tp, N_seg + 1, endpoint=True)
    tau = 2 * t / Tp - 1
    phi_c = 0

    total_flip = 0

    if fm_type == 'none':

        def F_offset(x):
            return 0
    else:

        def F_offset(x):
            return FM(x)

    for n in range(N_seg):
        # Calculate amplitude and phase at corresponding point (beginning pt.)
        next_pulse = make_block_pulse(flip_angle=2 * pi * AM(tau[n]) * T_seg,
                                      duration=T_seg,
                                      freq_offset=-F_offset(tau[n]),
                                      phase_offset=phi_c)
        list_RFs.append(next_pulse)
        total_flip += 2 * pi * AM(tau[n]) * T_seg
        dphi_bar, _ = integrate.quad(FM, a=tau[n], b=tau[n + 1])
        phi_c = phi_c + dphi_bar * 2 * pi
    print(f'Total flip angle from AM: {total_flip}')

    return list_RFs
Beispiel #2
0
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": -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, TR = 200e-3, 1000e-3
duration_to_center = (Nx / 2 + 0.5) * calc_duration(gx) + 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)
Beispiel #3
0
def write_UTE_3D_rf_spoiled(N=64,
                            FOV=250e-3,
                            slab_thk=250e-3,
                            FA=10,
                            TR=10e-3,
                            ro_asymmetry=0.97,
                            os_factor=1,
                            rf_type='sinc',
                            rf_dur=1e-3,
                            use_half_pulse=True,
                            save_seq=True):
    """
    Parameters
    ----------
    N : int, default=64
        Matrix size
    FOV : float, default=0.25
        Field-of-view in [meters]
    slab_thk : float, default=0.25
        Slab thickness in [meters]
    FA : float, default=10
        Flip angle in [degrees]
    TR : float, default=0.01
        Repetition time in [seconds]
    ro_asymmetry : float, default=0.97
        The ratio A/B where a A/(A+B) portion of 2*Kmax is omitted and B/(A+B) is acquired.
    os_factor : float, default=1
        Oversampling factor in readout
        The number of readout samples is the nearest integer from os_factor*N
    rf_type : str, default='sinc'
        RF pulse shape - 'sinc', 'gauss', or 'rect'
    rf_dur : float, default=0.001
        RF pulse duration
    use_half_pulse : bool, default=True
        Whether to use half pulse excitation for shorter TE;
        This doubles both the number of excitations and acquisition time
    save_seq : bool, default=True
        Whether to save this sequence as a .seq file

    Returns
    -------
    seq : Sequence
        PyPulseq 2D UTE sequence object
    TE : float
        Echo time of generated sequence in [seconds]
    ktraj : np.ndarray
        3D k-space trajectory [spoke, readout sample, 3] where
        the last dimension refers to spatial frequency coordinates (kx, ky, kz)
    """

    # Adapted from pypulseq demo write_ute.py (obtained mid-2021)
    system = Opts(max_grad=32,
                  grad_unit='mT/m',
                  max_slew=130,
                  slew_unit='T/m/s',
                  rf_ringdown_time=30e-6,
                  rf_dead_time=100e-6,
                  adc_dead_time=20e-6)
    seq = Sequence(system=system)

    # Derived parameters
    dx = FOV / N
    Ns, Ntheta, Nphi = get_radk_params_3D(dx, FOV)  # Make this function
    print(f'Using {Ns} spokes, with {Ntheta} thetas and {Nphi} phis')

    # Spoke angles
    thetas = np.linspace(0, np.pi, Ntheta, endpoint=False)
    phis = np.linspace(0, 2 * np.pi, Nphi, endpoint=False)

    ro_duration = 2.5e-3
    ro_os = os_factor  # Oversampling factor
    rf_spoiling_inc = 117  # RF spoiling increment value.

    if use_half_pulse:
        cp = 1
    else:
        cp = 0.5

    tbw = 2

    # Sequence components
    if rf_type == 'sinc':
        rf, gz, gz_reph = make_sinc_pulse(flip_angle=FA * np.pi / 180,
                                          duration=rf_dur,
                                          slice_thickness=slab_thk,
                                          apodization=0.5,
                                          time_bw_product=tbw,
                                          center_pos=cp,
                                          system=system,
                                          return_gz=True)
        gz_ramp_reph = make_trapezoid(channel='z',
                                      area=-gz.fall_time * gz.amplitude / 2,
                                      system=system)

    elif rf_type == 'rect':
        rf = make_block_pulse(flip_angle=FA * np.pi / 180,
                              duration=rf_dur,
                              slice_thickness=slab_thk,
                              return_gz=False)
    elif rf_type == 'gauss':
        rf, gz, gz_reph = make_gauss_pulse(flip_angle=FA * np.pi / 180,
                                           duration=rf_dur,
                                           slice_thickness=slab_thk,
                                           system=system,
                                           return_gz=True)
        gz_ramp_reph = make_trapezoid(channel='z',
                                      area=-gz.fall_time * gz.amplitude / 2,
                                      system=system)

    # Asymmetry! (0 - fully rewound; 1 - hall-echo)
    Nro = np.round(ro_os * N)  # Number of readout points
    s = np.round(ro_asymmetry * Nro / 2) / (Nro / 2)
    dk = (1 / FOV) / (1 + s)
    ro_area = N * dk
    gro = make_trapezoid(channel='x',
                         flat_area=ro_area,
                         flat_time=ro_duration,
                         system=system)
    adc = make_adc(num_samples=Nro,
                   duration=gro.flat_time,
                   delay=gro.rise_time,
                   system=system)
    gro_pre = make_trapezoid(channel='x',
                             area=-(gro.area - ro_area) / 2 - (ro_area / 2) *
                             (1 - s),
                             system=system)

    # Spoilers
    gro_spoil = make_trapezoid(channel='x', area=0.2 * N * dk, system=system)

    # Calculate timing
    TE = gro.rise_time + adc.dwell * Nro / 2 * (1 - s)
    if rf_type == 'sinc' or rf_type == 'gauss':
        if use_half_pulse:
            TE += gz.fall_time + calc_duration(gro_pre)
        else:
            TE += calc_duration(gz) / 2 + calc_duration(gro_pre)

        delay_TR = np.ceil(
            (TR - calc_duration(gro_pre) - calc_duration(gz) -
             calc_duration(gro)) / seq.grad_raster_time) * seq.grad_raster_time
    elif rf_type == 'rect':
        TE += calc_duration(gro_pre)
        delay_TR = np.ceil((TR - calc_duration(gro_pre) - calc_duration(gro)) /
                           seq.grad_raster_time) * seq.grad_raster_time
    assert np.all(delay_TR >= calc_duration(gro_spoil)
                  )  # The TR delay starts at the same time as the spoilers!
    print(f'TE = {TE * 1e6:.0f} us')

    C = int(use_half_pulse) + 1

    # Starting RF phase and increments
    rf_phase = 0
    rf_inc = 0
    Nline = Ntheta * Nphi
    ktraj = np.zeros([Nline, int(adc.num_samples), 3])
    u = 0

    for th in range(Ntheta):
        for ph in range(Nphi):
            unit_grad = np.zeros(3)
            unit_grad[0] = np.sin(thetas[th]) * np.cos(phis[ph])
            unit_grad[1] = np.sin(thetas[th]) * np.sin(phis[ph])
            unit_grad[2] = np.cos(thetas[th])
            # Two repeats if using half pulse
            for c in range(C):
                # RF spoiling
                rf.phase_offset = (rf_phase / 180) * np.pi
                adc.phase_offset = (rf_phase / 180) * np.pi
                rf_inc = np.mod(rf_inc + rf_spoiling_inc, 360.0)
                rf_phase = np.mod(rf_phase + rf_inc, 360.0)

                # Rewinder and readout gradients, vectorized
                gpx, gpy, gpz = make_oblique_gradients(gro_pre, unit_grad)
                grx, gry, grz = make_oblique_gradients(gro, unit_grad)
                gsx, gsy, gsz = make_oblique_gradients(gro_spoil, unit_grad)

                if rf_type == 'sinc' or rf_type == 'gauss':
                    if use_half_pulse:
                        # Reverse slice select amplitude (always z = 0)
                        modify_gradient(gz, scale=-1)
                        modify_gradient(gz_ramp_reph, scale=-1)
                        gpz_reph = copy.deepcopy(gpz)
                        modify_gradient(gpz_reph,
                                        scale=(gpz.area + gz_ramp_reph.area) /
                                        gpz.area)
                    else:
                        gpz_reph = copy.deepcopy(gpz)
                        modify_gradient(gpz_reph,
                                        scale=(gpz.area + gz_reph.area) /
                                        gpz.area)

                    seq.add_block(rf, gz)
                    seq.add_block(gpx, gpy, gpz_reph)

                elif rf_type == 'rect':
                    seq.add_block(rf)
                    seq.add_block(gpx, gpy, gpz)

                seq.add_block(grx, gry, grz, adc)
                seq.add_block(gsx, gsy, gsz, make_delay(delay_TR))

            #print(f'Spokes: {u+1}/{Nline}')
            ktraj[u, :, :] = get_ktraj_3d(grx, gry, grz, adc, [gpx], [gpy],
                                          [gpz])
            u += 1

    ok, error_report = seq.check_timing(
    )  # Check whether the timing of the sequence is correct
    if ok:
        print('Timing check passed successfully')
    else:
        print('Timing check failed. Error listing follows:')
        [print(e) for e in error_report]

    if save_seq:
        seq.write(
            f'ute_3d_rf-{rf_type}_rw_s{s}_N{N}_FOV{FOV}_TR{TR}_TE{TE}_C={use_half_pulse+1}.seq'
        )
        savemat(
            f'ktraj_ute_3d_rw_s{s}_N{N}_FOV{FOV}_TR{TR}_TE{TE}_C={use_half_pulse+1}.mat',
            {'ktraj': ktraj})

    return seq, TE, ktraj
Beispiel #4
0
# ===========
# PREPARATION
# ===========

# spoiler
spoil_amp = 0.8 * sys.max_grad  # Hz/m
rise_time = 1.0e-3  # spoiler rise time in seconds
spoil_dur = 6.5e-3  # complete spoiler duration in seconds

gx_spoil, gy_spoil, gz_spoil = [make_trapezoid(channel=c, system=sys, amplitude=spoil_amp, duration=spoil_dur,
                                               rise_time=rise_time) for c in ['x', 'y', 'z']]

# RF pulses
flip_angle_sat = seq_defs['b1cwpe'] * gamma_hz * 2 * np.pi * seq_defs['tp']
rf_pulse = make_block_pulse(flip_angle=flip_angle_sat, duration=seq_defs['tp'], system=sys)

# ADC events
pseudo_adc = make_adc(num_samples=1, duration=1e-3)  # (not played out; just used to split measurements)

# DELAYS
trec_delay = make_delay(seq_defs['trec'])
m0_delay = make_delay(seq_defs['trec_m0'])

# Sequence object
seq = Sequence()

# ===
# RUN
# ===