
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "generated/autoexamples/03-sequence-optimization/03-rf-pulse-design.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download_generated_autoexamples_03-sequence-optimization_03-rf-pulse-design.py>`
        to download the full example code or to run this example in your browser via Binder.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_generated_autoexamples_03-sequence-optimization_03-rf-pulse-design.py:


========================================
Designing an RF pulse by optimal control
========================================

The scope of this notebook is to design the samples of an RF pulse directly,
by gradient descent through a Bloch simulation of what they do [1]_.

A slice-selective pulse is designed for one transmit field. Where B1 varies --
by a fifth either way across a body at 3 T -- the flip inside the slice varies
with it. Here a 90 degree excitation is reshaped so that it stays as close to
90 degrees across that range as its samples allow.

.. [1] Conolly S, Nishimura D, Macovski A. Optimal control solutions to the
   magnetic resonance selective excitation problem. IEEE Trans Med Imaging
   1986;5(2):106-115.

.. GENERATED FROM PYTHON SOURCE LINES 20-24

.. colab-link::
   :needs_gpu: 0

   !pip install torchsim

.. GENERATED FROM PYTHON SOURCE LINES 24-57

.. code-block:: Python



    import math

    import torch

    from torchsim import SequenceDesign, compose_spinor








.. GENERATED FROM PYTHON SOURCE LINES 58-70

Pulse and slice
---------------

The pulse is ``SAMPLES`` samples long, and under its slice-select gradient
each sample turns a spin at ``x`` slice thicknesses from the centre by
``2 pi TBW x / SAMPLES`` about z. Its drive is in radians per sample, so
the design is stated without a raster or a gradient amplitude: any pulse of
this time-bandwidth product plays it.

The starting point is a Hamming-windowed sinc with the area of a 90 degree
flip -- a small-tip design, played at a large tip.


.. GENERATED FROM PYTHON SOURCE LINES 71-82

.. code-block:: Python


    SAMPLES, TBW = 128, 4.0
    x = torch.linspace(-2.0, 2.0, 161, dtype=torch.float64)
    turn = 2 * math.pi * TBW / SAMPLES * x

    t = torch.arange(SAMPLES, dtype=torch.float64) - (SAMPLES - 1) / 2
    sinc = torch.sinc(TBW * t / SAMPLES) * (
        0.54 + 0.46 * torch.cos(2 * math.pi * t / SAMPLES)
    )
    start = sinc / sinc.sum() * (math.pi / 2)








.. GENERATED FROM PYTHON SOURCE LINES 83-89

Transmit field
--------------

Every spin is simulated at five transmit scalings, from 0.8 to 1.2 of
nominal. The pulse is one; what it does at each scaling is not.


.. GENERATED FROM PYTHON SOURCE LINES 90-101

.. code-block:: Python


    B1 = torch.tensor([0.8, 0.9, 1.0, 1.1, 1.2], dtype=torch.float64)


    def excited(real, imag):
        """``|Mxy|`` after the pulse, from ``+z``: ``(B1, x)``."""
        drive = (real + 1j * imag)[:, None, None] * B1[None, :, None]
        a, b = compose_spinor(drive, turn.expand(len(B1), -1))
        return (2 * a.conj() * b).abs()









.. GENERATED FROM PYTHON SOURCE LINES 102-109

The cost
--------

Inside the slice the magnetisation should be all transverse, outside it
untouched; the transition band between is left free. A small penalty on the
pulse's energy keeps it from buying flatness with power.


.. GENERATED FROM PYTHON SOURCE LINES 110-122

.. code-block:: Python


    inside = (x.abs() < 0.4).double()
    outside = (x.abs() > 0.75).double()


    def cost(real, imag):
        transverse = excited(real, imag)
        miss = inside * (transverse - 1.0) ** 2 + outside * transverse**2
        energy = (real**2 + imag**2).sum() / (start**2).sum()
        return miss.sum() / (inside.sum() + outside.sum()) / len(B1) + 1e-4 * energy









.. GENERATED FROM PYTHON SOURCE LINES 123-130

Optimized pulse
---------------

The real and imaginary parts of every sample are the designed parameters,
free of limits: the scanner's peak B1 would be a :class:`~torchsim.Bounded`
on them.


.. GENERATED FROM PYTHON SOURCE LINES 131-159

.. code-block:: Python


    design = SequenceDesign(
        cost, real=start.clone(), imag=torch.zeros(SAMPLES, dtype=torch.float64)
    )
    result = design.minimize(iterations=100, learning_rate=2e-4)
    real, imag = result.parameters["real"], result.parameters["imag"]

    with torch.no_grad():
        before = excited(start, torch.zeros_like(start))
        after = excited(real, imag)





.. image-sg:: /generated/autoexamples/03-sequence-optimization/images/sphx_glr_03-rf-pulse-design_001.png
   :alt: Starting sinc, Optimized, Cost
   :srcset: /generated/autoexamples/03-sequence-optimization/images/sphx_glr_03-rf-pulse-design_001.png
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 160-163

Inside the slice the flip now varies less across the transmit range, most of
all where B1 is low, while outside it the leakage stays at the sinc's level:


.. GENERATED FROM PYTHON SOURCE LINES 164-171

.. code-block:: Python


    centre = inside.bool()
    for label, profile in (("starting sinc", before), ("optimized", after)):
        mean = profile[:, centre].mean(dim=1)
        print(
            f"{label:>14}: |Mxy| in the slice at each B1 {[round(float(v), 3) for v in mean]}"
        )




.. rst-class:: sphx-glr-script-out

 .. code-block:: none

     starting sinc: |Mxy| in the slice at each B1 [0.895, 0.94, 0.967, 0.976, 0.969]
         optimized: |Mxy| in the slice at each B1 [0.945, 0.981, 0.996, 0.989, 0.963]





.. rst-class:: sphx-glr-timing

   **Total running time of the script:** (0 minutes 6.639 seconds)


.. _sphx_glr_download_generated_autoexamples_03-sequence-optimization_03-rf-pulse-design.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: binder-badge

      .. image:: images/binder_badge_logo.svg
        :target: https://mybinder.org/v2/gh/firmlab-pisa/torchsim/gh-pages?urlpath=lab/tree/v0.0.8/examples/generated/autoexamples/03-sequence-optimization/03-rf-pulse-design.ipynb
        :alt: Launch binder
        :width: 150 px

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: 03-rf-pulse-design.ipynb <03-rf-pulse-design.ipynb>`

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: 03-rf-pulse-design.py <03-rf-pulse-design.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: 03-rf-pulse-design.zip <03-rf-pulse-design.zip>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_
