{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "MiKvRj5u076V"
   },
   "source": [
    "## **ABOUT**\n",
    "This example illustrates the 2D multi-slice, Spin Echo (SE) acquisition using the `pypulseq` library. This sequence is typically used for T<sub>2</sub> weighted imaging. A 2D Fourier transform can be used to reconstruct images from this acquisition. Read more about SE [here](http://mriquestions.com/se-vs-multi-se-vs-fse.html).\n",
    "\n",
    "**Contact**: For issues, write to ks3621@columbia.edu\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Y98YDJr215fa"
   },
   "source": [
    "## **INSTALL** `pypulseq`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "ogKNAZH3TmgA"
   },
   "outputs": [],
   "source": [
    "!pip install git+https://github.com/imr-framework/pypulseq.git@dev"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "UgqzEwle2xCd"
   },
   "source": [
    "## **IMPORT PACKAGES**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "3X7UsV832B6j"
   },
   "outputs": [],
   "source": [
    "from math import pi\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "from pypulseq.Sequence.sequence import Sequence\n",
    "from pypulseq.calc_duration import calc_duration\n",
    "from pypulseq.make_adc import make_adc\n",
    "from pypulseq.make_delay import make_delay\n",
    "from pypulseq.make_sinc_pulse import make_sinc_pulse\n",
    "from pypulseq.make_trapezoid import make_trapezoid\n",
    "from pypulseq.opts import Opts\n",
    "from pypulseq.calc_rf_center import calc_rf_center"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "UQ4AWw9l4et_"
   },
   "source": [
    "## **USER INPUTS**\n",
    "\n",
    "These parameters are typically on the user interface of the scanner computer console "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "ssnNwiQH4q_0"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "User inputs setup\n"
     ]
    }
   ],
   "source": [
    "nsa = 1  # Number of averages\n",
    "n_slices = 1  # Number of slices\n",
    "Nx = 16\n",
    "Ny = 16\n",
    "fov = 220e-3  # mm\n",
    "slice_thickness = 5e-3  # s\n",
    "slice_gap = 15e-3  # s\n",
    "rf_flip = 90  # degrees\n",
    "rf_offset = 0\n",
    "print('User inputs setup')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "PeYeI0V45ZfD"
   },
   "source": [
    "## **SYSTEM LIMITS**\n",
    "Set the hardware limits and initialize sequence object"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "XHs1LT965kqg"
   },
   "outputs": [],
   "source": [
    "system = Opts(max_grad=32, grad_unit='mT/m', max_slew=130, slew_unit='T/m/s', \n",
    "              grad_raster_time=10e-6, rf_ringdown_time=10e-6, \n",
    "              rf_dead_time=100e-6)\n",
    "seq = Sequence(system)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "ee-xBrpa7Zyn"
   },
   "source": [
    "## **TIME CONSTANTS**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "u2dW2nRf7obq"
   },
   "outputs": [],
   "source": [
    "TE = 100e-3  # s\n",
    "TR = 3  # s\n",
    "tau = TE / 2  # s\n",
    "readout_time = 6.4e-3\n",
    "pre_time = 8e-4  # s"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "OTw7M03g79bH"
   },
   "source": [
    "## **RF**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "XDZyQrbL8I3Q"
   },
   "outputs": [],
   "source": [
    "flip90 = round(rf_flip * pi / 180, 3)\n",
    "flip180 = 180 * pi / 180\n",
    "rf90, gz90, _ = make_sinc_pulse(flip_angle=flip90, system=system, duration=4e-3, \n",
    "                                slice_thickness=slice_thickness, apodization=0.5, \n",
    "                                time_bw_product=4, return_gz = True)\n",
    "rf180, gz180, _ = make_sinc_pulse(flip_angle=flip180, system=system, \n",
    "                                  duration=2.5e-3, \n",
    "                                  slice_thickness=slice_thickness, \n",
    "                                  apodization=0.5, \n",
    "                                  time_bw_product=4, phase_offset=90 * pi/180, return_gz = True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "RFSHuUOG9LHK"
   },
   "source": [
    "## **READOUT**\n",
    "Readout gradients and related events"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Q8p-CttI9dk9"
   },
   "outputs": [],
   "source": [
    "delta_k = 1 / fov\n",
    "k_width = Nx * delta_k\n",
    "gx = make_trapezoid(channel='x', system=system, flat_area=k_width, \n",
    "                    flat_time=readout_time)\n",
    "adc = make_adc(num_samples=Nx, duration=gx.flat_time, delay=gx.rise_time)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "o829kzm8kVFB"
   },
   "source": [
    "## **PREPHASE AND REPHASE**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "m5zA1bMakTVs"
   },
   "outputs": [],
   "source": [
    "phase_areas = (np.arange(Ny) - (Ny / 2)) * delta_k\n",
    "gz_reph = make_trapezoid(channel='z', system=system, area=-gz90.area / 2,\n",
    "                         duration=2.5e-3)\n",
    "gx_pre = make_trapezoid(channel='x', system=system, flat_area=k_width / 2, \n",
    "                        flat_time=readout_time / 2)\n",
    "gy_pre = make_trapezoid(channel='y', system=system, area=phase_areas[-1], \n",
    "                        duration=2e-3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "5Css5esAkYHo"
   },
   "source": [
    "## **SPOILER**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "R1DOmoKKkawr"
   },
   "outputs": [],
   "source": [
    "gz_spoil = make_trapezoid(channel='z', system=system, area=gz90.area * 4,\n",
    "                          duration=pre_time * 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "3F5JUpE9-4lo"
   },
   "source": [
    "## **DELAYS**\n",
    "Echo time (TE) and repetition time (TR). Here, TE is broken down into `delay1` and `delay2`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "aOKRJclb_mDQ"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "delay_1: namespace(type='delay', delay=0.04283000000000001)\n",
      "delay_2: namespace(type='delay', delay=0.04283000000000001)\n",
      "delay_TR: namespace(type='delay', delay=2.8927400000000003)\n"
     ]
    }
   ],
   "source": [
    "delay1 = tau - calc_duration(rf90) / 2 - calc_duration(gx_pre)\n",
    "delay1 -= calc_duration(gz_spoil) - calc_duration(rf180) / 2\n",
    "delay1 = np.ceil(delay1/seq.grad_raster_time)*seq.grad_raster_time\n",
    "delay1 = make_delay(delay1)\n",
    "\n",
    "delay2 = tau - calc_duration(rf180) / 2 - calc_duration(gz_spoil)\n",
    "delay2 -= calc_duration(gx) / 2\n",
    "delay2 = np.ceil(delay2/seq.grad_raster_time)*seq.grad_raster_time\n",
    "delay2 = make_delay(delay2)\n",
    "\n",
    "delay_TR = TR - calc_duration(rf90) / 2 - calc_duration(gx) / 2 - TE\n",
    "delay_TR -= calc_duration(gy_pre)\n",
    "delay_TR = np.ceil(delay_TR/seq.grad_raster_time)*seq.grad_raster_time\n",
    "delay_TR = make_delay(delay_TR)\n",
    "\n",
    "print(f'delay_1: {delay1}')\n",
    "print(f'delay_2: {delay1}')\n",
    "print(f'delay_TR: {delay_TR}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "6Dq4wT-UAEOR"
   },
   "source": [
    "## **CONSTRUCT SEQUENCE**\n",
    "Construct sequence for one phase encode and multiple slices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "B8ZmVkkrAXnK"
   },
   "outputs": [],
   "source": [
    "# Prepare RF offsets. This is required for multi-slice acquisition\n",
    "delta_z = n_slices * slice_gap\n",
    "z = np.linspace((-delta_z / 2), (delta_z / 2), n_slices) + rf_offset\n",
    "\n",
    "for k in range(nsa):  # Averages\n",
    "  for j in range(n_slices):  # Slices\n",
    "    # Apply RF offsets\n",
    "    freq_offset = gz90.amplitude * z[j]\n",
    "    rf90.freq_offset = freq_offset\n",
    "\n",
    "    freq_offset = gz180.amplitude * z[j]\n",
    "    rf180.freq_offset = freq_offset\n",
    "\n",
    "    for i in range(Ny):  # Phase encodes\n",
    "      seq.add_block(rf90, gz90)\n",
    "      gy_pre = make_trapezoid(channel='y', system=system, \n",
    "                              area=phase_areas[-i -1], duration=2e-3)\n",
    "      seq.add_block(gx_pre, gy_pre, gz_reph)\n",
    "      seq.add_block(delay1)\n",
    "      seq.add_block(gz_spoil)\n",
    "      seq.add_block(rf180, gz180)\n",
    "      seq.add_block(gz_spoil)\n",
    "      seq.add_block(delay2)\n",
    "      seq.add_block(gx, adc)\n",
    "      gy_pre = make_trapezoid(channel='y', system=system, \n",
    "                              area=-phase_areas[-i -1], duration=2e-3)\n",
    "      seq.add_block(gy_pre, gz_spoil)\n",
    "      seq.add_block(delay_TR)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "29.545454545454547"
      ]
     },
     "execution_count": 114,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "phase_areas[-1 -1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Prepare RF offsets. This is required for multi-slice acquisition\n",
    "rf90_phase = np.pi / 2\n",
    "rf180_phase = 0\n",
    "\n",
    "delta_z = n_slices * slice_gap\n",
    "z = np.linspace((-delta_z / 2), (delta_z / 2), n_slices) + rf_offset\n",
    "phase_areas = (np.arange(Ny) - (Ny / 2)) * delta_k\n",
    "for k in range(nsa):  # Averages\n",
    "  for j in range(n_slices):  # Slices\n",
    "    rf90.freq_offset = (gz180.amplitude * slice_thickness * (j - (n_slices - 1) / 2))\n",
    "    rf180.freq_offset = (gz180.amplitude * slice_thickness * (j - (n_slices - 1) / 2))\n",
    "    rf90.phase_offset = (rf90_phase - 2 * np.pi * rf90.freq_offset * calc_rf_center(rf90)[0])\n",
    "    rf180.phase_offset = (rf180_phase - 2 * np.pi * rf180.freq_offset * calc_rf_center(rf180)[0])\n",
    "\n",
    "    for i in range(Ny):  # Phase encodes\n",
    "      seq.add_block(rf90, gz90)\n",
    "      gy_pre = make_trapezoid(channel='y', system=system, \n",
    "                              area=phase_areas[-i -1], duration=2e-3)\n",
    "      seq.add_block(gx_pre, gy_pre, gz_reph)\n",
    "      seq.add_block(delay1)\n",
    "      seq.add_block(gz_spoil)\n",
    "      seq.add_block(rf180, gz180)\n",
    "      seq.add_block(gz_spoil)\n",
    "      seq.add_block(delay2)\n",
    "      seq.add_block(gx, adc)\n",
    "      gy_pre = make_trapezoid(channel='y', system=system, \n",
    "                              area=-phase_areas[-i -1], duration=2e-3)\n",
    "      seq.add_block(gy_pre, gz_spoil)\n",
    "      seq.add_block(delay_TR)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "l-YP9djBJCpC"
   },
   "source": [
    "## **PLOTTING TIMNG DIAGRAM**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 115,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "d_iCUR4nfoH9"
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 37600 characters]",
      "text/plain": [
       "<Figure size 640x480 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 34252 characters]",
      "text/plain": [
       "<Figure size 640x480 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "seq.plot(time_range=(0, 0.125))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "fYNgdWc_KiK7"
   },
   "source": [
    "## **GENERATING `.SEQ` FILE**\n",
    "Uncomment the code in the cell below to generate a `.seq` file and download locally."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 116,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "6iN0aeuuqKRe"
   },
   "outputs": [],
   "source": [
    " seq.write('C:\\\\MRI_seq\\\\new_MRI_pulse_seq\\\\t2_SE\\\\t2_SE_matrx16x16.seq')  # Save to disk\n",
    "# from google.colab import files\n",
    "# files.download('t2_se_pypulseq_colab.seq')  # Download locally"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 117,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "4Q0b5w-lKtfP"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(<xml.etree.ElementTree.ElementTree at 0x20ea7fdba30>,\n",
       " 'C:\\\\MRI_seq\\\\new_MRI_pulse_seq\\\\t2_SE/t2_SE_matrix16x16.xml')"
      ]
     },
     "execution_count": 117,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from py2jemris.seq2xml import seq2xml\n",
    "seq2xml(seq, seq_name='t2_SE_matrix16x16', out_folder='C:\\\\MRI_seq\\\\new_MRI_pulse_seq\\\\t2_SE')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "namespace(type='rf',\n",
       "          signal=array([-9.52099400e-09, -2.57194516e-07, -1.19130264e-06, ...,\n",
       "                        -1.19130264e-06, -2.57194516e-07, -9.52099400e-09]),\n",
       "          t=array([5.0000e-07, 1.5000e-06, 2.5000e-06, ..., 3.9975e-03, 3.9985e-03,\n",
       "                   3.9995e-03]),\n",
       "          shape_dur=0.004,\n",
       "          freq_offset=-1500.0,\n",
       "          phase_offset=0,\n",
       "          dead_time=0.0001,\n",
       "          ringdown_time=1e-05,\n",
       "          delay=0.00010000000000000002)"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rf90\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "write_t2_se.ipynb",
   "private_outputs": true,
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
