
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "generated/gallery/08-tours/02_segmented_epi.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

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

        :ref:`Go to the end <sphx_glr_download_generated_gallery_08-tours_02_segmented_epi.py>`
        to download the full example code.

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

.. _sphx_glr_generated_gallery_08-tours_02_segmented_epi.py:


========================
Segmented echo planar
========================

A segmented echo planar readout divides the phase-encode lines of the matrix
over several excitations (shots), each acquiring an interleaved subset with a
phase-encode blip between its echoes. The shot count determines both the scan
time and the off-resonance displacement in the image. The measured
relationship is the bandwidth per pixel along the phase-encode direction,
which increases in proportion to the number of shots, against the number of
excitations and hence the scan time.

**Prerequisites:** Course lesson 5, :doc:`Echo planar imaging </generated/gallery/03-gre-to-epi/03_epi>`.

Objectives
----------

After this example, you should be able to:

- create a phase-encode blip from the number of lines it advances and the
  field of view;
- assemble the blocks of one shot, with each blip in the block of the readout
  gradient it follows;
- verify from the k-space analysis that interleaved shots cover every line
  once;
- compute the phase-encode bandwidth per pixel and the off-resonance
  displacement as functions of the shot count.

.. GENERATED FROM PYTHON SOURCE LINES 30-40








.. GENERATED FROM PYTHON SOURCE LINES 41-49

Blips between the echoes
------------------------

Within a shot, consecutive echoes are separated by as many k-space lines as
there are shots, so that the shots interleave and together cover every line.
:func:`~pypulseqpp.make_phase_blip` takes that number of lines and the field
of view rather than an area, and solves the shortest gradient that delivers
it.

.. GENERATED FROM PYTHON SOURCE LINES 49-110

.. code-block:: Python


    import numpy as np

    import pypulseqpp as pp

    system = pp.Opts(
        max_grad=32.0,
        grad_unit="mT/m",
        max_slew=130.0,
        slew_unit="T/m/s",
        rf_dead_time=100e-6,
        rf_ringdown_time=20e-6,
        adc_dead_time=10e-6,
    )

    FOV = 220e-3
    MATRIX = 64
    THICKNESS = 5e-3
    FLIP_ANGLE_DEG = 90.0
    REPETITION_TIME = 100e-3
    DWELL = 4e-6
    SHOTS = 4

    rf, gz, gz_reph = pp.make_sinc_pulse(
        flip_angle=np.deg2rad(FLIP_ANGLE_DEG),
        duration=2e-3,
        slice_thickness=THICKNESS,
        apodization=0.5,
        time_bw_product=4.0,
        delay=system.rf_dead_time,
        system=system,
        use="excitation",
        return_gz=True,
    )

    # A readout of alternating polarity at a fixed dwell time.
    acquisition = MATRIX * DWELL
    raster = system.grad_raster_time
    gx = pp.make_trapezoid(
        channel="x",
        amplitude=MATRIX / FOV / acquisition,
        flat_time=raster * np.ceil(acquisition / raster),
        system=system,
    )
    adc = pp.make_adc(num_samples=MATRIX, dwell=DWELL, delay=gx.rise_time, system=system)
    gx_pre = pp.make_trapezoid(
        channel="x",
        area=-(gx.amplitude * gx.rise_time / 2 + (MATRIX / 2 + 0.5) / FOV),
        duration=5e-4,
        system=system,
    )

    blip = pp.make_phase_blip(channel="y", fov=FOV, steps=SHOTS, system=system)

    print(
        f"echo spacing {1e3 * pp.calc_duration(gx):.3f} ms, "
        f"blip {1e6 * pp.calc_duration(blip):.0f} us for {SHOTS} lines, "
        f"readout {1e3 * gx.amplitude / 42.576e6:.1f} mT/m "
        f"rising in {1e6 * gx.rise_time:.0f} us"
    )





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

 .. code-block:: none

    echo spacing 0.700 ms, blip 120 us for 4 lines, readout 26.7 mT/m rising in 220 us




.. GENERATED FROM PYTHON SOURCE LINES 111-119

One shot
--------

A shot is the excitation, the prewinders, and then one block per echo. The
blip is played in the same block as the readout gradient it follows, on the
other axis, so the echo spacing remains equal to the duration of one readout
gradient. The phase-encode prewinder of each shot moves the k-space position
to the first line of that shot.

.. GENERATED FROM PYTHON SOURCE LINES 119-166

.. code-block:: Python



    def segmented(shots, lines=MATRIX):
        """A segmented echo planar acquisition covering ``lines`` k-space lines."""
        step = pp.make_phase_blip(channel="y", fov=FOV, steps=shots, system=system)
        train = lines // shots
        seq = pp.Sequence(system=system)
        for shot in range(shots):
            start = pp.make_trapezoid(
                channel="y", area=(-lines / 2 + shot) / FOV, duration=5e-4, system=system
            )
            played = (
                pp.calc_duration(rf, gz)
                + pp.calc_duration(gx_pre, start, gz_reph)
                + train * pp.calc_duration(gx)
            )
            seq.add_block(rf, gz)
            seq.add_block(gx_pre, start, gz_reph)
            for echo in range(train):
                readout = pp.scale_grad(gx, (-1.0) ** echo)
                # The blip advances k-space before every echo but the first, and
                # plays inside the rise of the readout gradient it shares a block
                # with, so it is complete before the acquisition window opens.
                if echo == 0:
                    seq.add_block(readout, adc)
                else:
                    seq.add_block(readout, step, adc)
            seq.add_block(
                pp.make_delay(
                    pp.round_to_raster(
                        REPETITION_TIME - played, system.block_duration_raster
                    )
                )
            )
        return seq


    seq = segmented(SHOTS)

    ok, errors = seq.check_timing()
    print(
        f"timing {ok}, {seq.num_blocks} blocks, {MATRIX // SHOTS} echoes per shot, "
        f"{seq.duration()[0]:.3f} s"
    )

    seq.paper_plot(tr=1)




.. image-sg:: /generated/gallery/08-tours/images/sphx_glr_02_segmented_epi_001.png
   :alt: 02 segmented epi
   :srcset: /generated/gallery/08-tours/images/sphx_glr_02_segmented_epi_001.png
   :class: sphx-glr-single-img


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

 .. code-block:: none

    timing True, 76 blocks, 16 echoes per shot, 0.400 s




.. GENERATED FROM PYTHON SOURCE LINES 167-172

The raster the shots build
--------------------------

Each excitation contributes every fourth line, and the four together cover
the matrix, each line once.

.. GENERATED FROM PYTHON SOURCE LINES 172-184

.. code-block:: Python


    k_adc = seq.calculate_kspace()[0]
    # Every sample of one echo shares that echo's phase-encode line.
    lines = np.round(k_adc[1] * FOV).astype(int)
    covered, samples = np.unique(lines, return_counts=True)
    print(
        f"{covered.size} lines from {covered.min()} to {covered.max()}, "
        f"each acquired {(samples // MATRIX).min()} to {(samples // MATRIX).max()} times"
    )

    pp.plot.plot_kspace(seq, color_by="shot", plane="xy")




.. image-sg:: /generated/gallery/08-tours/images/sphx_glr_02_segmented_epi_002.png
   :alt: 02 segmented epi
   :srcset: /generated/gallery/08-tours/images/sphx_glr_02_segmented_epi_002.png
   :class: sphx-glr-single-img


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

 .. code-block:: none

    64 lines from -32 to 31, each acquired 1 to 1 times




.. GENERATED FROM PYTHON SOURCE LINES 185-202

Distortion against shot count
-----------------------------

Off-resonance displaces a voxel along the phase-encode direction by the ratio
of its offset to the bandwidth per pixel in that direction. That bandwidth is
not the receiver bandwidth: the phase-encode direction is traversed one line
per echo, so one line takes an echo spacing divided by the number of shots,
and the bandwidth per pixel is

.. math::

    \mathrm{BW}_\mathrm{pe} = \frac{S}{N\,\mathrm{ESP}} ,

with :math:`S` shots, :math:`N` lines and an echo spacing
:math:`\mathrm{ESP}`. It is smaller than the receiver bandwidth per pixel by
the number of echoes in a shot, which is why an echo planar image is
distorted along the phase-encode direction and not along the readout.

.. GENERATED FROM PYTHON SOURCE LINES 202-277

.. code-block:: Python


    OFF_RESONANCE_HZ = 100.0
    COUNTS = (1, 2, 4, 8, 16)

    echo_spacing = pp.calc_duration(gx)

    trade_off = []
    for shots in COUNTS:
        phase_bandwidth = shots / (MATRIX * echo_spacing)
        trade_off.append(
            {
                "shots": shots,
                "echoes": MATRIX // shots,
                "train": (MATRIX // shots) * echo_spacing,
                "phase_bandwidth": phase_bandwidth,
                "displacement": OFF_RESONANCE_HZ / phase_bandwidth,
                "scan": shots * REPETITION_TIME,
            }
        )





.. image-sg:: /generated/gallery/08-tours/images/sphx_glr_02_segmented_epi_003.png
   :alt: 02 segmented epi
   :srcset: /generated/gallery/08-tours/images/sphx_glr_02_segmented_epi_003.png
   :class: sphx-glr-single-img


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

 .. code-block:: none


     shots   echoes       train     BW per pixel   displacement   per slice
         1       64    44.80 ms          22.3 Hz        4.48 px      100 ms
         2       32    22.40 ms          44.6 Hz        2.24 px      200 ms
         4       16    11.20 ms          89.3 Hz        1.12 px      400 ms
         8        8     5.60 ms         178.6 Hz        0.56 px      800 ms
        16        4     2.80 ms         357.1 Hz        0.28 px     1600 ms




.. GENERATED FROM PYTHON SOURCE LINES 278-290

Displacement and echo train fall as the reciprocal of the shot count, and the
scan time rises in proportion to it, so the segmentation is a straight
exchange of time for geometric fidelity. The other terms of it are the echo
spacing, which a higher receiver bandwidth shortens and
which enters the displacement in the same way, and the number of lines, which
the prescription fixes.

Segmentation does not address two effects. Each shot is excited separately, so any
motion or phase change between them appears as an inconsistency between
interleaved lines rather than as blurring within one; and the displacement it
reduces is a property of the trajectory, not of the reconstruction, so an
image acquired in one shot is distorted whatever is done to it afterwards.


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

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


.. _sphx_glr_download_generated_gallery_08-tours_02_segmented_epi.py:

.. only:: html

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

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

      :download:`Download Jupyter notebook: 02_segmented_epi.ipynb <02_segmented_epi.ipynb>`

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

      :download:`Download Python source code: 02_segmented_epi.py <02_segmented_epi.py>`

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

      :download:`Download zipped: 02_segmented_epi.zip <02_segmented_epi.zip>`


.. only:: html

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

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