
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "generated/gallery/01-pulseq-basics/03_gradient_echo.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_01-pulseq-basics_03_gradient_echo.py>`
        to download the full example code.

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

.. _sphx_glr_generated_gallery_01-pulseq-basics_03_gradient_echo.py:


=============
Gradient echo
=============

The two previous lessons acquired signal without spatial encoding. This
lesson turns the experiment into a two-dimensional acquisition: the excitation
becomes slice-selective, the echo is formed by reversing a gradient rather than
by a refocusing pulse, and a phase encode changes the acquired k-space line
from one repetition to the next. Every Cartesian sequence in the later lessons
extends this structure.

The prewinder area determines the sample of the readout window at which the
k-space trajectory crosses zero, and the last section measures the echo time
that follows from it.

The representation these objects belong to is described in
:doc:`/explanations/pulseq/events-and-blocks`.

Learning objectives
-------------------

After this lesson, you should be able to:

- create a slice-selective excitation with its selection gradient and
  rephaser;
- derive the readout gradient, the prewinder and the phase-encode steps from
  the field of view and the matrix;
- assemble one repetition from blocks and pad it to the repetition time;
- read the sequence diagram and the Cartesian k-space raster;
- relate the prewinder area to the echo position and the echo time of an
  asymmetric echo.

.. GENERATED FROM PYTHON SOURCE LINES 34-44








.. GENERATED FROM PYTHON SOURCE LINES 45-52

Prescription
------------

The field of view and the matrix fix the k-space extent and the sample
spacing: a line spans :math:`N/\mathrm{FOV}` in 1/m and is sampled every
:math:`1/\mathrm{FOV}`. Nothing below reads a length in metres except through
those two numbers.

.. GENERATED FROM PYTHON SOURCE LINES 52-73

.. 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 = 128
    THICKNESS = 5e-3
    FLIP_ANGLE_DEG = 12.0
    REPETITION_TIME = 20e-3








.. GENERATED FROM PYTHON SOURCE LINES 74-85

Slice-selective excitation
--------------------------

A gradient played during the pulse makes the resonance frequency a function
of position, so the pulse's bandwidth selects a slab of the prescribed
thickness. Position within the slice maps to phase accumulated under the
second half of the selection lobe, which the rephaser unwinds; without it the
signal integrates to nothing across the slice.

With ``return_gz=True`` the factory also returns the selection gradient and
the rephaser.

.. GENERATED FROM PYTHON SOURCE LINES 85-104

.. code-block:: Python


    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,
    )

    print(
        f"selection {1e3 * gz.amplitude / 42.576e6:.2f} mT/m, "
        f"bandwidth {4.0 / 2e-3 * 1e-3:.1f} kHz, "
        f"rephaser {1e3 * pp.calc_duration(gz_reph):.2f} ms"
    )





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

 .. code-block:: none

    selection 9.39 mT/m, bandwidth 2.0 kHz, rephaser 0.56 ms




.. GENERATED FROM PYTHON SOURCE LINES 105-116

Readout and phase encoding
--------------------------

The readout gradient is prescribed by the area its flat top has to cover, and
the acquisition window is delayed into that flat top by the rise time, so
that every sample is taken at constant amplitude. The dwell and the flat time
come from :func:`~pypulseqpp.calc_adc_timing`, which puts the one on the ADC
raster and the other on the gradient raster. The prewinder moves the k-space
position to one end of the line before the readout traverses it, and the phase encode displaces the line
perpendicular to it; one phase-encode step is :math:`1/\mathrm{FOV}`, so the
largest of them is half the k-space extent.

.. GENERATED FROM PYTHON SOURCE LINES 116-143

.. code-block:: Python


    dwell, readout_time = pp.calc_adc_timing(
        MATRIX,
        26e-6,
        grad_raster_time=system.grad_raster_time,
        adc_raster_time=system.adc_raster_time,
    )

    gx = pp.make_trapezoid(
        channel="x", flat_area=MATRIX / FOV, flat_time=readout_time, 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.area / 2, duration=1e-3, system=system)
    gy_pre = pp.make_trapezoid(
        channel="y", area=MATRIX / (2 * FOV), duration=1e-3, system=system
    )

    # One line per phase encode, from one edge of k-space to the other.
    phase_encodes = np.linspace(-1.0, 1.0, MATRIX, endpoint=False)

    print(
        f"readout {1e3 * gx.amplitude / 42.576e6:.2f} mT/m, "
        f"dwell {1e6 * adc.dwell:.1f} us, "
        f"receiver bandwidth {1e-3 / adc.dwell:.1f} kHz, "
        f"{1 / (adc.dwell * MATRIX):.0f} Hz per pixel"
    )





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

 .. code-block:: none

    readout 3.56 mT/m, dwell 30.0 us, receiver bandwidth 33.3 kHz, 260 Hz per pixel




.. GENERATED FROM PYTHON SOURCE LINES 144-152

One repetition
--------------

Four blocks: the pulse with its selection gradient, the prewinders and the
rephaser together, the readout with the acquisition window, and a delay that
brings the repetition up to the prescribed repetition time.
:func:`~pypulseqpp.scale_grad` takes the largest phase encode to the step
this repetition acquires.

.. GENERATED FROM PYTHON SOURCE LINES 152-185

.. code-block:: Python



    def gradient_echo(prewinder_fraction=0.5):
        """The sequence, with the prewinder at the given fraction of the line."""
        prewinder = pp.scale_grad(gx_pre, 2 * prewinder_fraction)
        played = (
            pp.calc_duration(rf, gz)
            + pp.calc_duration(prewinder, gy_pre, gz_reph)
            + pp.calc_duration(gx, adc)
        )
        seq = pp.Sequence(system=system)
        for step in phase_encodes:
            seq.add_block(rf, gz)
            seq.add_block(prewinder, pp.scale_grad(gy_pre, step), gz_reph)
            seq.add_block(gx, adc)
            seq.add_block(
                pp.make_delay(
                    pp.round_to_raster(
                        REPETITION_TIME - played, system.block_duration_raster
                    )
                )
            )
        return seq


    seq = gradient_echo()

    ok, errors = seq.check_timing()
    print(
        f"timing {ok}, {seq.num_blocks} blocks, "
        f"{seq.duration()[0]:.3f} s for {MATRIX} lines"
    )





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

 .. code-block:: none

    timing True, 512 blocks, 2.560 s for 128 lines




.. GENERATED FROM PYTHON SOURCE LINES 186-190

Sequence diagram and k-space
----------------------------

One repetition, drawn against the others it repeats.

.. GENERATED FROM PYTHON SOURCE LINES 190-193

.. code-block:: Python


    seq.paper_plot(tr=1)




.. image-sg:: /generated/gallery/01-pulseq-basics/images/sphx_glr_03_gradient_echo_001.png
   :alt: 03 gradient echo
   :srcset: /generated/gallery/01-pulseq-basics/images/sphx_glr_03_gradient_echo_001.png
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 194-196

The phase encodes make the raster; the prewinder and the ramps carry k-space
between the lines and are not sampled.

.. GENERATED FROM PYTHON SOURCE LINES 196-199

.. code-block:: Python


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




.. image-sg:: /generated/gallery/01-pulseq-basics/images/sphx_glr_03_gradient_echo_002.png
   :alt: 03 gradient echo
   :srcset: /generated/gallery/01-pulseq-basics/images/sphx_glr_03_gradient_echo_002.png
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 200-215

Asymmetric echo
---------------

The prewinder area sets how much of the line the readout covers before the
echo. Moving the k-space position only part of the way to the edge and shortening the readout by
the same amount keeps the far edge of the line where it was, so the
resolution is unchanged, and removes samples from the near side, which a
partial-Fourier reconstruction then has to supply from conjugate symmetry.
The echo time falls by the time those samples would have taken.

A sample advances k-space by :math:`1/\mathrm{FOV}`, so the prewinder has to
cancel the ramp of the readout gradient plus one such step per sample taken
before the echo. The additional half step is the offset from the start of
the window to the centre of its first sample; including it in the prewinder
places the echo on a sample rather than between two.

.. GENERATED FROM PYTHON SOURCE LINES 215-309

.. code-block:: Python


    HALF_LINE = MATRIX // 2
    FRACTIONS = (1.0, 0.75, 0.5, 0.25)


    def asymmetric_readout(fraction):
        """The readout, its window and its prewinder, with a partial near side."""
        before = round(fraction * HALF_LINE)
        samples = before + HALF_LINE
        readout = pp.make_trapezoid(
            channel="x",
            amplitude=gx.amplitude,
            flat_time=pp.round_to_raster(samples * dwell, system.grad_raster_time),
            system=system,
        )
        window = pp.make_adc(
            num_samples=samples, dwell=dwell, delay=readout.rise_time, system=system
        )
        ramp_area = readout.amplitude * readout.rise_time / 2
        prewinder = pp.make_trapezoid(
            channel="x",
            area=-(ramp_area + (before + 0.5) / FOV),
            duration=pp.calc_duration(gx_pre),
            system=system,
        )
        return readout, window, prewinder


    def one_repetition(fraction):
        """A single repetition, at the largest phase encode."""
        readout, window, prewinder = asymmetric_readout(fraction)
        seq = pp.Sequence(system=system)
        seq.add_block(rf, gz)
        seq.add_block(prewinder, gy_pre, gz_reph)
        seq.add_block(readout, window)
        return seq


    measured = []
    for fraction in FRACTIONS:
        k_adc, _, t_excitation, _, t_adc = one_repetition(fraction).calculate_kspacePP()
        echo = int(np.argmin(np.abs(k_adc[0])))
        measured.append(
            {
                "fraction": fraction,
                "sample": echo,
                "samples": k_adc.shape[1],
                "echo_time": t_adc[echo] - t_excitation[0],
                "kx": 2 * k_adc[0] * FOV / MATRIX,
            }
        )





.. image-sg:: /generated/gallery/01-pulseq-basics/images/sphx_glr_03_gradient_echo_003.png
   :alt: 03 gradient echo
   :srcset: /generated/gallery/01-pulseq-basics/images/sphx_glr_03_gradient_echo_003.png
   :class: sphx-glr-single-img


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

 .. code-block:: none


     near side    samples    echo at     echo time
          1.00        128         64       4.05 ms
          0.75        112         48       3.58 ms
          0.50         96         32       3.09 ms
          0.25         80         16       2.62 ms




.. GENERATED FROM PYTHON SOURCE LINES 310-320

Every line reaches the same :math:`+k_\mathrm{max}`, so all four have the
resolution the matrix prescribes; they differ in how far the near side is
measured and in when the echo occurs. Acquiring a quarter of the near side
removes three quarters of the samples on that side and shortens the echo time
by the amount the figure reports.

The repetition time is unchanged throughout, so a shorter echo time here
reduces the signal decay before the echo is measured; it does not shorten the
scan. Shortening the scan is the subject of
:doc:`/generated/gallery/03-gre-to-epi/02_segmented`.


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

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


.. _sphx_glr_download_generated_gallery_01-pulseq-basics_03_gradient_echo.py:

.. only:: html

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

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

      :download:`Download Jupyter notebook: 03_gradient_echo.ipynb <03_gradient_echo.ipynb>`

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

      :download:`Download Python source code: 03_gradient_echo.py <03_gradient_echo.py>`

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

      :download:`Download zipped: 03_gradient_echo.zip <03_gradient_echo.zip>`


.. only:: html

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

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