Пример #1
0
def generate_relaxation_pulse(relaxation_time, delta_t):
    """Returns a relaxation pulse
    Params:
        relaxation_time: (positive float) total time for relaxation pulse
        delta_t: (positive float) time step
    Returns:
        pulse_relax: (Pulse object) relaxation pulse
    """
    # Check params
    if not (isinstance(relaxation_time, float) and relaxation_time > 0):
        raise TypeError("relaxation_time must be a positive float")
    if not (isinstance(delta_t, float) and delta_t > 0):
        raise TypeError("delta_t must be a positive float")
    # Compute relaxation pulse
    relax_length = int(
        relaxation_time / delta_t
    )  # wait long enough for transverse magnetization to decay and for longitudinal magnetization to rebuild
    Gx = np.zeros(relax_length)
    Gy = np.zeros(relax_length)
    Gz = np.zeros(relax_length)
    readout = np.zeros(relax_length, dtype=bool)
    pulse_relax = Pulse(mode='free',
                        Gx=Gx,
                        Gy=Gy,
                        Gz=Gz,
                        readout=readout,
                        delta_t=delta_t)
    return pulse_relax
Пример #2
0
def generate_tipping_pulse(gyromagnetic_ratio, main_field, Gz_amplitude,
                           slice_width, tip_angle, delta_t):
    """Generates the tipping pulse for a spin-echo sequence
    Params:
        gyromagnetic_ratio: (positive float) gyromagnetic ratio of species
        main_field: (positive float) main magnetic field
        Gz_amplitude: (positive float) z-gradient amplitude
        slice_width: (postive float) width of the slice
        tip_angle: (float \in (0,pi)) angle to tip
        delta_t: (positive float) time step
    Returns:
        pulse_tip: (list of Pulse objects) the pulse required to tip the magnetization in a slice
    """
    # Check params
    if not (isinstance(gyromagnetic_ratio, float) and gyromagnetic_ratio > 0):
        raise TypeError('gyromagnetic_ratio must be a positive float')
    if not (isinstance(main_field, float) and main_field > 0):
        raise TypeError('main_field must be a positive float')
    if not (isinstance(Gz_amplitude, float) and Gz_amplitude > 0):
        raise TypeError('Gz_amplitude must be a positive float')
    if not (isinstance(slice_width, float) and slice_width > 0):
        raise TypeError('slice_width must be a positive float')
    if not (isinstance(tip_angle, float) and tip_angle > 0
            and tip_angle < np.pi):
        raise TypeError('tip_angle must be a float between 0 and \pi')
    if not (isinstance(delta_t, float) and delta_t > 0):
        raise TypeError('delta_t must be a positive float')
    # Tipping pulse
    Gz_tip_amp = Gz_amplitude
    pulse_tip_duration = 2 * np.pi * 8 / (gyromagnetic_ratio * Gz_tip_amp *
                                          (slice_width / 2.0))
    t_vals = np.arange(0.0, pulse_tip_duration, delta_t)
    t_vals_shifted = t_vals - pulse_tip_duration / 2.0
    sinc_scaling = 2.0 * 8.0 / pulse_tip_duration
    gaussian_std = pulse_tip_duration / 3.0
    B1x_tip = np.sinc(sinc_scaling * t_vals_shifted) * np.exp(
        -(t_vals_shifted**2) / (2 * gaussian_std**2))  # sinc pulse
    alpha_origin = gyromagnetic_ratio * sum(B1x_tip) * delta_t
    desired_alpha_origin = tip_angle
    B1x_amplitude_scaling = desired_alpha_origin / alpha_origin
    B1x_tip = B1x_tip * B1x_amplitude_scaling  # rescale for desired tip angle
    pulse_tip_length = len(B1x_tip)
    B1y_tip = np.zeros(pulse_tip_length)
    Gz_tip = Gz_tip_amp * np.ones(pulse_tip_length)
    omega_rf = gyromagnetic_ratio * main_field  # omega_rf = omega_0
    pulse_tip = Pulse(mode='excite',
                      delta_t=delta_t,
                      B1x=B1x_tip,
                      B1y=B1y_tip,
                      Gz=Gz_tip,
                      omega_rf=omega_rf)

    return pulse_tip
# Tipping pulse
B1x_tip = np.sinc(sinc_scaling * t_vals_shifted) * np.exp(
    -(t_vals_shifted**2) / (2 * gaussian_std**2))  # sinc pulse
alpha_origin = em_gyromagnetic_ratio * sum(B1x_tip) * delta_t
desired_alpha_origin = 0.52
B1x_amplitude_scaling = desired_alpha_origin / alpha_origin
B1x_tip = B1x_tip * B1x_amplitude_scaling  # rescale for desired tip angle
pulse_tip_length = len(B1x_tip)
B1y_tip = np.zeros(pulse_tip_length)
Gz_tip_amp = 5.0 * 1e-3  # T/m
Gz_tip = Gz_tip_amp * np.ones(pulse_tip_length)
omega_rf = em_gyromagnetic_ratio * main_field  # omega_rf = omega_0
pulse_tip = Pulse(mode='excite',
                  delta_t=delta_t,
                  B1x=B1x_tip,
                  B1y=B1y_tip,
                  Gz=Gz_tip,
                  omega_rf=omega_rf)

# Refocusing pulse
pulse_refocus_length = int(pulse_tip_length / 2)
Gx_refocus = np.zeros(pulse_refocus_length)
Gy_refocus = np.zeros(pulse_refocus_length)
Gz_refocus = -Gz_tip_amp * np.ones(pulse_refocus_length)
Gz_no_refocus = np.zeros(pulse_refocus_length)
readout = np.zeros(pulse_refocus_length, dtype=bool)
pulse_refocus = Pulse(mode='free',
                      delta_t=delta_t,
                      Gx=Gx_refocus,
                      Gy=Gy_refocus,
                      Gz=Gz_refocus,
Пример #4
0
from pypulse import Pulse
from pysim import Sim
import numpy as np
import matplotlib.pyplot as plt

# Declare a readout pulse
time_step = 0.01
pulse_length = 500
mode = 'free'
Gx = np.ones(pulse_length)
Gy = np.ones(pulse_length)
readout = np.zeros(pulse_length, dtype=bool)
for i in range(len(readout)):
    if not i % 2:
        readout[i] = True
pulse = Pulse(mode=mode, Gx=Gx, Gy=Gy, readout=readout, time_step=time_step)

# Initialize simulation
num_ems = 1
main_field = 10.0
pulse_sequence = [pulse]
sim = Sim(num_ems, main_field, pulse_sequence)

# Run simulation and collect MR signal
mr_signals = sim.run_sim()
mr_signal = mr_signals[0]

# Plot MR signal (FID of one em)
t_readout = []
for step_no in range(pulse_length):
    if readout[step_no]:
Пример #5
0
for pulse_duration in pulse_durations:

    # Declare excitation pulse
    delta_t = 0.01
    t_vals = np.arange(0.0, pulse_duration, delta_t)
    t_vals_shifted = t_vals - pulse_duration / 2.0
    B1x = np.sinc(pulse_duration * t_vals_shifted) * np.exp(
        -pulse_duration / 10 * (t_vals_shifted**2))
    pulse_length = len(B1x)
    B1y = np.zeros(pulse_length)
    Gz_amp = 20.0
    Gz = Gz_amp * np.ones(pulse_length)
    omega_rf = em_gyromagnetic_ratio * main_field  # omega_rf = omega_0
    pulse = Pulse(mode='excite',
                  delta_t=delta_t,
                  B1x=B1x,
                  B1y=B1y,
                  Gz=Gz,
                  omega_rf=omega_rf)

    # Compute theoretical magnetization profile
    B1x_fft = np.roll(abs(np.fft.fft(B1x)), int(len(B1x) / 2)) / np.sqrt(
        len(B1x))
    freq = np.fft.fftfreq(len(B1x), delta_t)
    freq = np.fft.fftshift(freq)
    z_theory = -2 * np.pi / em_gyromagnetic_ratio * (1.0 / Gz_amp) * freq
    m_theory = em_equilibrium_magnetization * B1x_fft

    # Compute theoretical tip angle at origin
    alpha_origin = em_gyromagnetic_ratio * sum(B1x) * delta_t
    print(alpha_origin)
Пример #6
0
def generate_dw_kspace_pulses(pulse_tip, fe_sample_radius, pe_sample_radius,
                              kx_max, ky_max, kmove_time, kread_time,
                              gyromagnetic_ratio, delta_t, read_all,
                              diffusion_gradients, diffusion_gradient_time,
                              diffusion_time):
    """Generates the set of pulses necessary to sample desired region of kspace using Cartesian sampling with diffusion weighting
    Params:
        pulse_tip: (Pulse object) tipping pulse in spin-echo sequence
        fe_sample_radius: (positive int) radius of number of samples in frequency-encoding direction
        pe_sample_radius: (positive int) radius of number of samples in phase-encoding direction
        kx_max: (positive float) maximum value of kx to sample
        ky_max: (positive float) maximum value of ky to sample
        kmove_time: (positive float) time to move to each location in kspace
        kread_time: (positive float) time for readout of kspace line
        gyromagnetic_ratio: (float) gyromagnetic ratio of species to be imaged
        delta_t: (positive float) time step for simulation
        read_all: (bool) if true, record the MR signal at all time steps during the read time
        diffusion_gradients: (numpy 3-vector of nonnegative floats) magnitude of diffusion gradient in each spatial direction
        diffusion_gradient_time: (positive float) time to apply diffusion gradient
        diffusion_time: (positive float) time between positive and negative lobe of diffusion gradient.
    Returns:
        kspace_pulses: (list of Pulse objects) set of readout pulses to sample given region of kspace
    Notes:
        kmove_time must be greater than half the tipping pulse time (see a 2DFT sequence diagram)
        the fact that num_fe_samples and num_pe_samples are odd numbers is necessary for shift_2DFT function
    """
    # Check params
    if not (isinstance(pulse_tip, Pulse)):
        raise TypeError("pulse_tip must be a Pulse object")
    if not (isinstance(fe_sample_radius, int) and fe_sample_radius > 0):
        raise TypeError("fe_sample_radius must be a positive int")
    if not (isinstance(pe_sample_radius, int) and pe_sample_radius > 0):
        raise TypeError("pe_sample_radius must be a positive int")
    if not (isinstance(kx_max, float) and kx_max > 0):
        raise TypeError("kx_max must be a positive float")
    if not (isinstance(ky_max, float) and ky_max > 0):
        raise TypeError("ky_max must be a positive float")
    if not (isinstance(kmove_time, float) and kmove_time > 0):
        raise TypeError("kmove_time must be a positive float")
    if not (isinstance(kread_time, float) and kread_time > 0):
        raise TypeError("kread_time must be a positive float")
    if not isinstance(gyromagnetic_ratio, float):
        raise TypeError("gyromagnetic_ratio must be a float")
    if not (isinstance(delta_t, float) and delta_t > 0):
        raise TypeError("delta_t must be a positive float")
    if not (isinstance(read_all, bool)):
        raise TypeError("read_all must be a bool")
    if not (kmove_time > pulse_tip.length * delta_t / 2.0):
        print("kmove_time: " + str(kmove_time * 1e3))
        print("pulse_tip time: " + str(pulse_tip.length * delta_t * 1e3 / 2.0))
        raise ValueError(
            "kmove_time must be greater than half the tipping pulse time -- see a 2DFT sequence diagram"
        )
    if not (diffusion_gradients.shape == (3, )
            and diffusion_gradients.dtype == np.float64
            and all([item >= 0.0 for item in diffusion_gradients])):
        raise TypeError(
            "diffusion_gradients must be a numpy 3-vector of nonnegative floats"
        )
    if not (isinstance(diffusion_gradient_time, float)
            and diffusion_gradient_time > 0.0):
        raise TypeError("diffusion_gradient_time must be a positive float")
    if not (isinstance(diffusion_time, float) and diffusion_time > 0.0):
        raise TypeError("diffusion_time must be a positive float")
    if not (diffusion_time > diffusion_gradient_time):
        raise ValueError(
            "diffusion_time must be larger than diffusion_gradient_time")
    # Compute number of pe and fe samples
    num_fe_samples = int(2 * fe_sample_radius + 1)
    num_pe_samples = int(2 * pe_sample_radius + 1)
    # Frequency-encoding direction parameters
    adc_step = int(kread_time / (num_fe_samples * delta_t))
    # Common parameters
    kread_len = (num_fe_samples - 1) * adc_step + 1
    kmove_len = int(kmove_time / delta_t)
    # Refocusing lobe in longitudinal direction
    pulse_tip_length = pulse_tip.length
    Gz_tip_amp = pulse_tip.Gz[0]
    pulse_refocus_length = int(pulse_tip_length / 2)
    Gz_refocus_lobe = -Gz_tip_amp * np.ones(pulse_refocus_length)
    Gz_kmove = np.concatenate(
        (Gz_refocus_lobe, np.zeros(kmove_len - pulse_refocus_length)))
    # Phase-encoding direction parameters
    delta_ky = (2 * ky_max) / (num_pe_samples - 1)
    # Compute pulses in frequency-encoding direction
    Gx_kmove_amp = -(2 * np.pi / gyromagnetic_ratio) * kx_max / (kmove_time)
    Gx_kmove = Gx_kmove_amp * np.ones(kmove_len)
    Gx_kread_amp = (2 * np.pi /
                    gyromagnetic_ratio) * (2 * kx_max) / (kread_len * delta_t)
    Gx_kread = Gx_kread_amp * np.ones(kread_len)
    # Compute diffusion-weighting pulses
    diff_len = int((diffusion_time + diffusion_gradient_time) / delta_t)
    diff_grad_len = int(diffusion_gradient_time / delta_t)
    diff_wait_len = diff_len - 2 * diff_grad_len
    Gx_diff = np.concatenate(
        (diffusion_gradients[0] * np.ones(diff_grad_len),
         np.zeros(diff_wait_len),
         -diffusion_gradients[0] * np.ones(diff_grad_len)))
    Gy_diff = np.concatenate(
        (diffusion_gradients[1] * np.ones(diff_grad_len),
         np.zeros(diff_wait_len),
         -diffusion_gradients[1] * np.ones(diff_grad_len)))
    Gz_diff = np.concatenate(
        (diffusion_gradients[2] * np.ones(diff_grad_len),
         np.zeros(diff_wait_len),
         -diffusion_gradients[2] * np.ones(diff_grad_len)))
    # Compute gradient in
    Gx = np.concatenate((Gx_kmove, Gx_diff, Gx_kread))
    # Readout indices
    if read_all:
        readout = np.ones(kread_len, dtype=bool)
    else:
        readout = np.zeros(kread_len, dtype=bool)
        for fe_sample_no in range(num_fe_samples):
            fe_sample_index = fe_sample_no * adc_step
            readout[fe_sample_index] = True
    readout = np.concatenate((np.zeros(kmove_len + diff_len,
                                       dtype=bool), readout))
    # Compute pulses in phase-encoding direction
    kspace_pulses = []
    for pe_sample_no in range(num_pe_samples):
        ky = ky_max - pe_sample_no * delta_ky
        # Gradients for movement to position in kspace
        Gy_kmove_amp = (2 * np.pi / gyromagnetic_ratio) * ky / (kmove_time)
        Gy_kmove = Gy_kmove_amp * np.ones(kmove_len)
        # Gradients for readout movement
        Gy_kread = np.zeros(kread_len, dtype=float)
        # Gradients for sampling one line of kspace
        Gy = np.concatenate((Gy_kmove, Gy_diff, Gy_kread))
        # Bundle into Pulse object
        Gz = np.concatenate((Gz_kmove, Gz_diff, np.zeros(kread_len)))
        pulse = Pulse(mode='free',
                      Gx=Gx,
                      Gy=Gy,
                      Gz=Gz,
                      readout=readout,
                      delta_t=delta_t)
        kspace_pulses.append(pulse)
    return kspace_pulses
Пример #7
0
def generate_dw_pulse(pulse_tip, kread_time, gyromagnetic_ratio, delta_t,
                      diffusion_gradients, diffusion_gradient_time,
                      diffusion_time):
    """Generates a diffusion-weighting pulse
    Params:
        pulse_tip: (Pulse object) tipping pulse in spin-echo sequence
        kread_time: (positive float) time for readout of kspace line
        gyromagnetic_ratio: (float) gyromagnetic ratio of species to be imaged
        delta_t: (positive float) time step for simulation
        read_all: (bool) if true, record the MR signal at all time steps during the read time
        diffusion_gradients: (numpy 3-vector of nonnegative floats) magnitude of diffusion gradient in each spatial direction
        diffusion_gradient_time: (positive float) time to apply diffusion gradient
        diffusion_time: (positive float) time between positive and negative lobe of diffusion gradient.
    Returns:
        pulse: a diffusion-weighting pulse
    """
    # Check params
    if not (isinstance(pulse_tip, Pulse)):
        raise TypeError("pulse_tip must be a Pulse object")
    if not (isinstance(kread_time, float) and kread_time > 0):
        raise TypeError("kread_time must be a positive float")
    if not isinstance(gyromagnetic_ratio, float):
        raise TypeError("gyromagnetic_ratio must be a float")
    if not (isinstance(delta_t, float) and delta_t > 0):
        raise TypeError("delta_t must be a positive float")
    if not (diffusion_gradients.shape == (3, )
            and diffusion_gradients.dtype == np.float64
            and all([item >= 0.0 for item in diffusion_gradients])):
        raise TypeError(
            "diffusion_gradients must be a numpy 3-vector of nonnegative floats"
        )
    if not (isinstance(diffusion_gradient_time, float)
            and diffusion_gradient_time > 0.0):
        raise TypeError("diffusion_gradient_time must be a positive float")
    if not (isinstance(diffusion_time, float) and diffusion_time > 0.0):
        raise TypeError("diffusion_time must be a positive float")
    if not (diffusion_time > diffusion_gradient_time):
        raise ValueError(
            "diffusion_time must be larger than diffusion_gradient_time")
    # Common parameters
    kread_len = int(kread_time / delta_t)
    # Refocusing lobe in longitudinal direction
    pulse_tip_length = pulse_tip.length
    Gz_tip_amp = pulse_tip.Gz[0]
    pulse_refocus_length = int(pulse_tip_length / 2)
    Gz_refocus_lobe = -Gz_tip_amp * np.ones(pulse_refocus_length)
    Gx_refocus_lobe = np.zeros(pulse_refocus_length)
    Gy_refocus_lobe = np.zeros(pulse_refocus_length)
    Gz_read = np.zeros(kread_len)
    Gy_read = np.zeros(kread_len)
    Gx_read = np.zeros(kread_len)
    # Compute diffusion-weighting pulses
    diff_len = int((diffusion_time + diffusion_gradient_time) / delta_t)
    diff_grad_len = int(diffusion_gradient_time / delta_t)
    diff_wait_len = diff_len - 2 * diff_grad_len
    Gx_diff = np.concatenate(
        (diffusion_gradients[0] * np.ones(diff_grad_len),
         np.zeros(diff_wait_len),
         -diffusion_gradients[0] * np.ones(diff_grad_len)))
    Gy_diff = np.concatenate(
        (diffusion_gradients[1] * np.ones(diff_grad_len),
         np.zeros(diff_wait_len),
         -diffusion_gradients[1] * np.ones(diff_grad_len)))
    Gz_diff = np.concatenate(
        (diffusion_gradients[2] * np.ones(diff_grad_len),
         np.zeros(diff_wait_len),
         -diffusion_gradients[2] * np.ones(diff_grad_len)))
    # Compute gradients
    Gz = np.concatenate((Gz_refocus_lobe, Gz_diff, Gz_read))
    Gy = np.concatenate((Gy_refocus_lobe, Gy_diff, Gy_read))
    Gx = np.concatenate((Gx_refocus_lobe, Gx_diff, Gx_read))
    # Readout indices
    readout = np.ones(kread_len, dtype=bool)
    readout = np.concatenate((np.zeros(pulse_refocus_length + diff_len,
                                       dtype=bool), readout))
    # Bundle into Pulse object
    pulse = Pulse(mode='free',
                  Gx=Gx,
                  Gy=Gy,
                  Gz=Gz,
                  readout=readout,
                  delta_t=delta_t)
    return pulse
Пример #8
0
B0 = 3.0
mu_eq = 1.0
sigma = 0.0

# Generate pulse sequence
t_final = 6.0
delta_t = 1.0e-2
t_vals = np.arange(0.0, t_final, delta_t)
pulse_length = len(t_vals)
Gx = np.ones(pulse_length)
Gy = np.zeros(pulse_length)
Gz = np.zeros(pulse_length)
readout = np.ones(pulse_length, dtype=bool)
pulse = Pulse(mode='free',
              Gx=Gx,
              Gy=Gy,
              Gz=Gz,
              readout=readout,
              delta_t=delta_t)

# Run simulation
em_magnetizations = np.array([mu0])
em_positions = np.array([r0])
em_velocities = np.array([v0])
em_gyromagnetic_ratio = gamma
em_shielding_constants = np.array([sigma])
em_equilibrium_magnetization = mu_eq


def T1_map(position):
    return 1e6