コード例 #1
0
 def __init__(self, shape='exp', theta=90, t_ramp=0.001):
     # ramp shape [string]
     self.shape = shape
     # relaxation during switch-off [logical]
     self.rds = False
     # pre-polarization B-field amplitude in units of [B0]
     self.factor = 100
     # initial orientation between PP and B0 [deg]
     self._theta = theta
     # azimuthal angle of pre-polarization field [deg]
     self._phi = 0
     # pre-polarization B-field switch amplitude in units of [B0]
     self.switch_factor = 1
     # switch-off ramp time [s]
     self.t_ramp = t_ramp
     # switch-off ramp slope time [s]
     self.t_slope = t_ramp / 10
     # calculate initial orientation
     self.orient = misc.get_orient_from_angles(self._theta, self._phi)
     # adiabatic quality p
     self.adiab_qual = None
     # plot data which is calculated later (only when needed)
     self.plot_t = None
     self.plot_amp = None
     self.plot_alpha = None
     self.plot_omega = None
     self.plot_dadt = None
コード例 #2
0
    def calc_plot_features(self):
        """Calculate all relevant ramp parameter for plotting."""
        # create dummy time vector just for plotting
        time = np.linspace(0, self.Ramp.t_ramp, 1001)
        delta_t = time[1] - time[0]
        # prepare plot variables
        bp_amp = np.linspace(0, self.Ramp.t_ramp, 1001)
        alpha = np.linspace(0, self.Ramp.t_ramp, 1001)
        omega = np.linspace(0, self.Ramp.t_ramp, 1001)
        dadt = np.linspace(0, self.Ramp.t_ramp, 1001)

        for i in np.arange(0, len(time)):
            # get the PP B-field amplitude
            bp_amp[i] = self.B0 * self.Ramp.get_ramp_amplitude(time[i])
            # Earth's magnetic field B0
            b_earth = np.array([0., 0., self.B0])
            # effective B-field Bp+B0
            b_eff = bp_amp[i] * self.Ramp.orient + b_earth
            # amplitude of the effective B-field [T]
            b_eff_n = np.sqrt(b_eff[0]**2 + b_eff[1]**2 + b_eff[2]**2)
            # angle between primary and pre-polarization field [rad]
            alpha[i] = misc.get_angle_between_vectors(b_earth, b_eff)
            # angular frequency of the effective B-field [rad/s]
            omega[i] = self.gamma * b_eff_n
            if i > 0:
                # rate of change of the angle alpha [rad/s]
                dadt[i - 1] = np.abs((alpha[i] - alpha[i - 1]) / delta_t)

        self.Ramp.plot_t = time * 1e3  # in [ms]
        self.Ramp.plot_amp = bp_amp / self.B0
        self.Ramp.plot_alpha = alpha
        self.Ramp.plot_omega = omega
        self.Ramp.plot_dadt = dadt
コード例 #3
0
    def plot_bloch_sphere(axis,
                          lonlat,
                          radius=1,
                          lon_range=(-180.0, 180.0),
                          lat_range=(-90.0, 90.0)):
        """Draw a unit sized Bloch Sphere."""
        # from the lon and lat increments get the longitudinal and latitudinal
        # circles in xyz coordinates
        lons, lats = misc.get_sphere_grid(lonlat,
                                          radius=radius,
                                          lon_range=lon_range,
                                          lat_range=lat_range)
        # first draw the longitudinal circles
        xlin = lons[0]
        ylin = lons[1]
        zlin = lons[2]
        for i in np.arange(0, xlin.shape[0]):
            axis.plot(xlin[i, :],
                      ylin[i, :],
                      zlin[i, :],
                      color='darkgray',
                      linewidth=1)
        # then draw the latitudinal circles
        xlin = lats[0]
        ylin = lats[1]
        zlin = lats[2]
        for i in np.arange(0, xlin.shape[0]):
            axis.plot(xlin[i, :],
                      ylin[i, :],
                      zlin[i, :],
                      color='darkgray',
                      linewidth=1)

        # draw the inner lines
        axis.plot([-radius, radius], [0, 0], [0, 0],
                  color='darkgray',
                  linewidth=1)
        axis.plot([0, 0], [-radius, radius], [0, 0],
                  color='darkgray',
                  linewidth=1)
        axis.plot([0, 0], [0, 0], [-radius, radius],
                  color='darkgray',
                  linewidth=1)
        # draw the main axes
        axis.plot([0, radius * 1.2], [0, 0], [0, 0], color='r', linewidth=1.3)
        axis.plot([0, 0], [0, radius * 1.2], [0, 0], color='g', linewidth=1.3)
        axis.plot([0, 0], [0, 0], [0, radius * 1.2], color='b', linewidth=1.3)
        # draw the axis label
        axis.text(radius * 1.3, 0, 0, "X", color='r', ha='center')
        axis.text(0, radius * 1.3, 0, "Y", color='g', ha='center')
        axis.text(0, 0, radius * 1.3, "Z", color='b', ha='center')
        # set the limits
        axis.set_xlim(-1, 1)
        axis.set_ylim(-1, 1)
        axis.set_zlim(-1, 1)
        # set the view
        axis.view_init(azim=45, elev=30)
        # remove the axis lines
        axis.axis('off')
コード例 #4
0
    def bloch_fcn(self, time, mag):
        """Bloch equation to be solved by the ode-solver."""
        if time <= self.Ramp.t_ramp:
            # get PP B-field amplitude
            # the Ramp amplitude is just a factor, hence the multiplication
            # with B0 to make it a value in [T]
            bp_amp = self.B0 * self.Ramp.get_ramp_amplitude(time)
            # Earth's magnetic field B0
            b_earth = np.array([0., 0., self.B0])
            # PP magnetic field is the Bp amplitude times the orientation
            # vector
            b_prepol = bp_amp * self.Ramp.orient
            # merge both B-fields
            b_total = b_prepol + b_earth
            # if rds is off scale the relaxation times and therewith basically
            # switch them off
            if self.Ramp.rds is True:
                T1 = self.T1
                T2 = self.T2
            else:
                T1 = self.T1 * 1e6
                T2 = self.T2 * 1e6

            # if the B-field vector is not parallel to B0 rotate B and m
            # into z-axis to apply relaxation
            b_total_n = b_total / np.linalg.norm(b_total)
            if np.any(b_total_n is not self.zunit):
                rot_mat = misc.get_rotmat_from_vectors(b_total, self.zunit)
                b_total = rot_mat.dot(b_total)
                mag = rot_mat.dot(mag)
                # dM/dt
                dmagdt = self.gamma * np.cross(mag, b_total) - \
                    np.array([mag[0], mag[1], 0.]) / T2 - \
                    np.array([0., 0., mag[2] - self.M0[2]]) / T1
                rot_mat_transp = rot_mat.T
                dmagdt = rot_mat_transp.dot(dmagdt)

            else:
                # dM/dt
                dmagdt = self.gamma * np.cross(mag, b_total) - \
                    np.array([mag[0], mag[1], 0.]) / T2 - \
                    np.array([0., 0., mag[2] - self.M0[2]]) / T1
        else:
            # assemble B field only from B0
            b_total = np.array([0., 0., self.B0])

            # dM/dt
            dmagdt = self.gamma * np.cross(mag, b_total) - \
                np.array([mag[0], mag[1], 0.]) / self.T2 - \
                np.array([0., 0., mag[2] - self.M0[2]]) / self.T1
        return dmagdt
コード例 #5
0
    def solve(self, m_init, info=False, use_numba=False, atol=1e-9, rtol=1e-9):
        """Solve the Bloch equation with scipy 'solve_ivp'.

        Uses ‘RK45’: Explicit Runge-Kutta method of order 5(4) [1].
        The error is controlled assuming accuracy of the fourth-order method,
        but steps are taken using the fifth-order accurate formula (local
        extrapolation is done). A quartic interpolation polynomial is used for
        the dense output [2]. Can be applied in the complex domain.

        References
        ----------
        .. [1] J. R. Dormand, P. J. Prince, “A family of embedded Runge-Kutta
            formulae”, Journal of Computational and Applied Mathematics,
            Vol. 6, No. 1, pp. 19-26, 1980.
        .. [2] L. W. Shampine, “Some Practical Runge-Kutta Formulas”,
            Mathematics of Computation,, Vol. 46, No. 173, pp. 135-150, 1986.
        """
        if use_numba:
            self.solver = solve_ivp(self.bloch_fcn_numba, [0, self.t_sim],
                                    m_init,
                                    rtol=rtol,
                                    atol=atol)
        else:
            self.solver = solve_ivp(self.bloch_fcn, [0, self.t_sim],
                                    m_init,
                                    rtol=rtol,
                                    atol=atol)
        if info:
            print(self.solver.message)
        if self.solver.success:
            self.t = self.solver.t.T
            self.m = self.solver.y
            # transform magnetization into rotating frame of reference
            self.inst_phase = self.larmor_w * self.t
            self.calc_m_rot()
            # calculate FFT of lab-frame magnetization
            freq, spec = misc.get_fft(self.t, self.m[[0, 1], :], True)
            self.fft_freq = freq
            self.fft_spec = spec
        else:
            raise ValueError("Something went south during integration.")
コード例 #6
0
    def solve(self, m_init, info=False, use_numba=False, atol=1e-9, rtol=1e-9):
        """Solve the Bloch equation with scipy 'solve_ivp'.

        Uses ‘RK45’: Explicit Runge-Kutta method of order 5(4) [1].
        The error is controlled assuming accuracy of the fourth-order method,
        but steps are taken using the fifth-order accurate formula (local
        extrapolation is done). A quartic interpolation polynomial is used for
        the dense output [2]. Can be applied in the complex domain.

        References
        ----------
        .. [1] J. R. Dormand, P. J. Prince, “A family of embedded Runge-Kutta
            formulae”, Journal of Computational and Applied Mathematics,
            Vol. 6, No. 1, pp. 19-26, 1980.
        .. [2] L. W. Shampine, “Some Practical Runge-Kutta Formulas”,
            Mathematics of Computation,, Vol. 46, No. 173, pp. 135-150, 1986.
        """
        if use_numba:
            self.solver = solve_ivp(self.bloch_fcn_numba, [0, self.t_sim],
                                    m_init, rtol=rtol, atol=atol)
        else:
            self.solver = solve_ivp(self.bloch_fcn, [0, self.t_sim], m_init,
                                    rtol=rtol, atol=atol)
        if info:
            print(self.solver.message)
        if self.solver.success:
            self.t = self.solver.t.T
            self.m = self.solver.y
            # transform magnetization into rotating frame of reference
            # therefore calculate the instantaneous phase for all time steps
            # during the pulse
            if self.t_sim > self.Pulse.t_pulse:
                # standard phase
                self.inst_phase = self.larmor_w*self.t
                # auxiliary phase angle = 0
                phi_aux = np.zeros(len(self.t))
                # two logical mask for time steps during pulse (maks1) and
                # time steps after (mask2)
                mask1 = self.t <= self.Pulse.t_pulse
                mask2 = self.t > self.Pulse.t_pulse
                # calculate instantaneous phase during the pulse
                t_pulse = self.t[mask1]
                self.Pulse.fmod.get_modulated_phase(t_now=t_pulse)
                self.inst_phase[mask1] = self.Pulse.fmod.modulated_phase

                # auxiliary phase angle at the end of the pulse is used for
                # all time steps after the pulse
                phi_aux = np.zeros(len(self.t))
                self.Pulse.fmod.get_modulated_phase(t_now=self.Pulse.t_pulse,
                                                    flag=1)
                phi_aux[mask2] = self.Pulse.fmod.modulated_phase

                # now perform rot-frame transformation
                self.calc_m_rot(phi=phi_aux)
                # calculate pulse FFT
                b_pulse = self.Pulse.get_pulse_amplitude(t_pulse)
                freq, spec = misc.get_fft(t_pulse, b_pulse, True)
                self.Pulse.fft_freq = freq
                self.Pulse.fft_spec = spec
            else:
                # perform rot-frame transformation
                self.Pulse.fmod.t_now = self.t
                self.inst_phase = self.Pulse.fmod.modulated_phase
                self.calc_m_rot()
                # calculate pulse FFT
                b_pulse = self.Pulse.get_pulse_amplitude(self.t)
                freq, spec = misc.get_fft(self.t, b_pulse, True)
                self.Pulse.fft_freq = freq
                self.Pulse.fft_spec = spec

            # calculate FFT of lab-frame magnetization
            freq, spec = misc.get_fft(self.t, self.m[[0, 1], :], True)
            self.fft_freq = freq
            self.fft_spec = spec
        else:
            raise ValueError("Something went south during integration.")
コード例 #7
0
 def phi(self, value):
     self._phi = value
     self.orient = misc.get_orient_from_angles(self.theta, self._phi)
コード例 #8
0
 def theta(self, value):
     self._theta = value
     self.orient = misc.get_orient_from_angles(self._theta, self.phi)