Commit 6e5f1b9a by Mark Chiew

### Fixed weird bug w.r.t. ipynbs created in VS Code, switched ode to odeint

parent d74a8c2c
 ... ... @@ -2,9 +2,7 @@ "cells": [ { "cell_type": "markdown", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Imports" ] ... ... @@ -16,15 +14,13 @@ "outputs": [], "source": [ "import numpy as np\n", "from scipy.integrate import ode\n", "from scipy.integrate import ode, solve_ivp\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Define the Bloch equation\n", "\n", ... ... @@ -42,7 +38,7 @@ "outputs": [], "source": [ "# define bloch equation\n", "def bloch_ode(t, M, T1, T2):\n", "def bloch(t, M, T1, T2):\n", " # get effective B-field at time t\n", " B = B_eff(t)\n", " # cross product of M and B, add T1 and T2 relaxation terms\n", ... ... @@ -55,9 +51,7 @@ }, { "cell_type": "markdown", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Define the pulse sequence \n", "\n", ... ... @@ -102,9 +96,7 @@ }, { "cell_type": "markdown", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Plot the pulse sequence\n", "\n", ... ... @@ -139,18 +131,15 @@ }, { "cell_type": "markdown", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Integrate ODE \n", "\n", "This uses a Runge-Kutta variant called the \"Dormand-Prince method\"\n", "This uses a Runge-Kutta variant called the \"Dormand-Prince method\" by default. Other ode integration methods are available.\n", "\n", "In this section:\n", "- list of arrays\n", "- ode solvers\n", "- list appending" "- lambdas (anonymous functions)" ] }, { ... ... @@ -160,42 +149,30 @@ "outputs": [], "source": [ "# Set the initial conditions\n", "# time (t) = 0\n", "# equilibrium magnetization (M) = (0, 0, 1)\n", "t = [0]\n", "M = [np.array([0, 0, 1])]\n", "\n", "# Set integrator time-step\n", "dt= 0.005\n", "M = [0, 0, 1]\n", "\n", "# Set up ODE integrator object\n", "r = ode(bloch_ode)\n", "# Set time interval for integration\n", "t = [0, 5]\n", "\n", "# Choose the integrator method\n", "r.set_integrator('dopri5')\n", "\n", "# Pass in initial values\n", "r.set_initial_value(M[0], t[0])\n", "# Set max step size\n", "dt = 0.005\n", "\n", "# Set T1 and T2\n", "T1, T2 = 1500, 50\n", "r.set_f_params(T1, T2)\n", "\n", "# Integrate by looping over time, moving dt by step size each iteration\n", "# Append new time point and Magnetisation vector at every step to t and M\n", "while r.successful() and r.t < 5:\n", " t.append(r.t + dt)\n", " M.append(r.integrate(t[-1]))\n", "# Integrate ODE\n", "# In Scipy 1.2.0, the first argument to solve_ivp must be a function that takes exactly 2 arguments\n", "sol = solve_ivp(lambda t, M : bloch(t, M, T1, T2), t, M, max_step=dt)\n", "\n", "# Convert M to 2-D numpy array from list of arrays\n", "M = np.array(M)" "# Grab output\n", "t = sol.t\n", "M = sol.y" ] }, { "cell_type": "markdown", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Plot Results\n", "\n", ... ... @@ -213,9 +190,9 @@ "_, ax = plt.subplots(figsize=(12,12))\n", "\n", "# Plot x, y and z components of Magnetisation\n", "ax.plot(t, M[:,0], label='Mx')\n", "ax.plot(t, M[:,1], label='My')\n", "ax.plot(t, M[:,2], label='Mz')\n", "ax.plot(t, M[0,:], label='Mx')\n", "ax.plot(t, M[1,:], label='My')\n", "ax.plot(t, M[2,:], label='Mz')\n", "\n", "# Add legend and grid\n", "ax.legend()\n", ... ... @@ -239,9 +216,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3-final" "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 4 } \ No newline at end of file }
 %% Cell type:markdown id: tags: Imports %% Cell type:code id: tags: ``` python import numpy as np from scipy.integrate import ode from scipy.integrate import ode, solve_ivp import matplotlib.pyplot as plt ``` %% Cell type:markdown id: tags: # Define the Bloch equation \$\$\frac{d\vec{M}}{dt} = \vec{M}\times \vec{B} - \frac{M_x + M_y}{T2} - \frac{M_z - M_0}{T1}\$\$ In this section: - define a function - numpy functions like np.cross %% Cell type:code id: tags: ``` python # define bloch equation def bloch_ode(t, M, T1, T2): def bloch(t, M, T1, T2): # get effective B-field at time t B = B_eff(t) # cross product of M and B, add T1 and T2 relaxation terms return np.array([M[1]*B[2] - M[2]*B[1] - M[0]/T2, M[2]*B[0] - M[0]*B[2] - M[1]/T2, M[0]*B[1] - M[1]*B[0] - (M[2]-1)/T1]) # alternatively #return np.cross(M,B) - np.array([M[0]/T2, M[1]/T2, (M[2]-1)/T1]) ``` %% Cell type:markdown id: tags: # Define the pulse sequence We work in the rotating frame, so we only need the amplitude envelope of the RF pulse Typically, B1 excitation fields point in the x- and/or y-directions Gradient fields point in the z-direction In this simple example, a simple sinc-pulse excitation pulse is applied for 1 ms along the x-axis then a gradient is turned on for 1.5 ms after that. In this section: - constants such as np.pi - functions like np.sinc %% Cell type:code id: tags: ``` python # define effective B-field def B_eff(t): # Do nothing for 0.25 ms if t < 0.25: return np.array([0, 0, 0]) # Sinc RF along x-axis and slice-select gradient on for 1.00 ms elif t < 1.25: return np.array([np.pi*np.sinc((t-0.75)*4), 0, np.pi]) # Do nothing for 0.25 ms elif t < 1.50: return np.array([0, 0, 0]) # Slice refocusing gradient on for 1.50 ms # Half the area of the slice-select gradient lobe elif t < 3.00: return np.array([0, 0, -(1/3)*np.pi]) # Pulse sequence finished else: return np.array([0, 0, 0]) ``` %% Cell type:markdown id: tags: # Plot the pulse sequence In this section: - unpacking return values - unwanted return values - list comprehension - basic plotting %% Cell type:code id: tags: ``` python # Create 2 vertical subplots _, ax = plt.subplots(2, 1, figsize=(12,12)) # Get pulse sequence B-fields from 0 - 5 ms pulseq = [B_eff(t) for t in np.linspace(0,5,1000)] pulseq = np.array(pulseq) # Plot RF ax[0].plot(pulseq[:,0]) ax[0].set_ylabel('B1') # Plot gradient ax[1].plot(pulseq[:,2]) ax[1].set_ylabel('Gradient') ``` %% Cell type:markdown id: tags: # Integrate ODE This uses a Runge-Kutta variant called the "Dormand-Prince method" This uses a Runge-Kutta variant called the "Dormand-Prince method" by default. Other ode integration methods are available. In this section: - list of arrays - ode solvers - list appending - lambdas (anonymous functions) %% Cell type:code id: tags: ``` python # Set the initial conditions # time (t) = 0 # equilibrium magnetization (M) = (0, 0, 1) t = [0] M = [np.array([0, 0, 1])] M = [0, 0, 1] # Set integrator time-step dt= 0.005 # Set time interval for integration t = [0, 5] # Set up ODE integrator object r = ode(bloch_ode) # Choose the integrator method r.set_integrator('dopri5') # Pass in initial values r.set_initial_value(M[0], t[0]) # Set max step size dt = 0.005 # Set T1 and T2 T1, T2 = 1500, 50 r.set_f_params(T1, T2) # Integrate by looping over time, moving dt by step size each iteration # Append new time point and Magnetisation vector at every step to t and M while r.successful() and r.t < 5: t.append(r.t + dt) M.append(r.integrate(t[-1])) # Integrate ODE # In Scipy 1.2.0, the first argument to solve_ivp must be a function that takes exactly 2 arguments sol = solve_ivp(lambda t, M : bloch(t, M, T1, T2), t, M, max_step=dt) # Convert M to 2-D numpy array from list of arrays M = np.array(M) # Grab output t = sol.t M = sol.y ``` %% Cell type:markdown id: tags: # Plot Results In this section: - more plotting %% Cell type:code id: tags: ``` python # Create single axis _, ax = plt.subplots(figsize=(12,12)) # Plot x, y and z components of Magnetisation ax.plot(t, M[:,0], label='Mx') ax.plot(t, M[:,1], label='My') ax.plot(t, M[:,2], label='Mz') ax.plot(t, M[0,:], label='Mx') ax.plot(t, M[1,:], label='My') ax.plot(t, M[2,:], label='Mz') # Add legend and grid ax.legend() ax.grid() ``` ... ...
 ... ... @@ -2,9 +2,7 @@ "cells": [ { "cell_type": "markdown", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Imports" ] ... ... @@ -22,9 +20,7 @@ }, { "cell_type": "markdown", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Load data\n", "\n", ... ... @@ -57,9 +53,7 @@ }, { "cell_type": "markdown", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 6/8 Partial Fourier sampling\n", "\n", ... ... @@ -91,9 +85,7 @@ }, { "cell_type": "markdown", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Estimate phase\n", "\n", ... ... @@ -130,9 +122,7 @@ }, { "cell_type": "markdown", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# POCS reconstruction\n", "\n", ... ... @@ -192,9 +182,7 @@ }, { "cell_type": "markdown", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Display error and plot reconstruction\n", "\n", ... ... @@ -252,9 +240,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3-final" "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 4 } \ No newline at end of file }
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!