
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "generated/gallery/05-sequence-modules/03_sequence_app.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_05-sequence-modules_03_sequence_app.py>`
        to download the full example code.

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

.. _sphx_glr_generated_gallery_05-sequence-modules_03_sequence_app.py:


=========================
A sequence application
=========================

The two previous lessons designed an excitation and a readout with modules.
This lesson assembles them into a complete acquisition: a prescription, a
sampling order, and a kernel that is played once per repetition.
:class:`~pypulseqpp.sequences.SequenceApp` separates these three and is the
base class of every shipped sequence, so the application written here has the
same form as a sequence in :doc:`/sequences`.

The sequence is the RF-spoiled slice-selective gradient echo of
:doc:`/generated/gallery/02-spoiling/02_rf_spoiling`, expressed as an
application rather than as a loop over events. The architecture is described
in :doc:`/explanations/design/sequence-application`.

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

After this lesson, you should be able to:

- state the responsibilities of ``init_sequence``, ``loop``, ``kernel`` and
  ``finalize`` in a sequence application;
- subclass :class:`~pypulseqpp.sequences.SequenceApp` to design and play a
  sequence from a prescription;
- read the acquisition order back from the ``LIN`` labels of the sequence;
- derive the command-line flags of an application from its ``init_sequence``
  signature.

.. GENERATED FROM PYTHON SOURCE LINES 33-41

Application methods
-------------------

``init_sequence`` receives the prescription and designs the modules and the
sampling order from it. ``loop`` calls ``kernel`` once per repetition.
``finalize`` records the definitions a reconstruction reads. Settings a user
does not prescribe are class attributes, and ``MAX_GRAD`` and ``MAX_SLEW``
have no default, so every application states them.

.. GENERATED FROM PYTHON SOURCE LINES 41-134

.. code-block:: Python


    import numpy as np

    import pypulseqpp as pp
    import pypulseqpp.sequences as design
    from pypulseqpp.sequences import SequenceApp


    class SpoiledGradientEcho(SequenceApp):
        """RF-spoiled 2D Cartesian gradient echo, one line per repetition."""

        NAME = "tour_gre_2d"
        MAX_GRAD = 40.0
        MAX_SLEW = 150.0
        PULSE_DURATION = 3e-3
        RF_SPOILING_INCREMENT_DEG = 117.0

        def init_sequence(
            self,
            fov: float = 220e-3,
            matrix: int = 128,
            slice_thickness: float = 5e-3,
            flip_angle_deg: float = 12.0,
            te: float | None = None,
            tr: float | None = None,
        ) -> None:
            """Design the excitation, the readout and the line order.

            Parameters
            ----------
            fov : float, optional
                Isotropic field of view (m).
            matrix : int, optional
                Matrix size along both encoded axes.
            slice_thickness : float, optional
                Slice thickness (m).
            flip_angle_deg : float, optional
                Excitation flip angle (degrees).
            te : float | None, optional
                Echo time (s). ``None`` is as short as the readout admits.
            tr : float | None, optional
                Repetition time (s). ``None`` is as short as possible.
            """
            self.matrix = matrix
            self.excitation = design.SpatialSelectiveExcitation(
                self.system,
                flip_angle_deg=flip_angle_deg,
                thickness_m=slice_thickness,
                duration_s=self.PULSE_DURATION,
            )
            self.readout = design.LineReadout2D(
                self.system,
                self.excitation.rf,
                self.excitation.gz,
                self.excitation.gz_reph,
                fov=(fov, fov),
                matrix=(matrix, matrix),
                te=te,
                tr=tr,
            )
            self.lines = list(range(matrix))
            self.phases = np.deg2rad(self.RF_SPOILING_INCREMENT_DEG) * np.cumsum(
                np.arange(len(self.lines) + 1)
            )
            self.fov, self.slice_thickness = fov, slice_thickness

        def loop(self) -> None:
            """Play every phase-encode line in order."""
            for index, line in enumerate(self.lines):
                self.kernel(line, float(self.phases[index]))

        def kernel(self, line: int, phase: float) -> None:
            """One repetition: excitation, encoding, acquisition, spoiling."""
            readout = self.readout
            readout.rf.phase_offset = phase % (2 * np.pi)
            readout.adc.phase_offset = phase % (2 * np.pi)
            step = (line - self.matrix // 2) / (self.matrix / 2)
            self.seq.add_block(readout.rf, readout.gz, *self.labels(LIN=line))
            self.seq.add_block(
                readout.gx_pre, pp.scale_grad(readout.gy_pre, step), readout.gz_reph
            )
            self.seq.add_block(readout.gx, readout.adc)
            self.seq.add_block(readout.gx_spoil, pp.scale_grad(readout.gy_rew, step))
            if getattr(readout, "wait_tr", None) is not None:
                self.seq.add_block(readout.wait_tr)

        def finalize(self) -> None:
            """Record the geometry a reconstruction reads."""
            self.seq.set_definition("FOV", [self.fov, self.fov, self.slice_thickness])
            self.seq.set_definition("Matrix", [self.matrix, self.matrix, 1])
            self.seq.set_definition("Name", self.NAME)









.. GENERATED FROM PYTHON SOURCE LINES 135-141

Sequence construction
---------------------

Construction calls ``init_sequence`` to prepare the modules and sampling
order. ``design`` creates a new sequence, executes ``loop`` and records the
reconstruction definitions in ``finalize``.

.. GENERATED FROM PYTHON SOURCE LINES 141-148

.. code-block:: Python


    app = SpoiledGradientEcho(matrix=128)
    seq = app.design()
    print(f"{seq.num_blocks} blocks, {seq.duration()[0]:.2f} s")
    print("timing:", seq.check_timing()[0])
    print("definitions:", sorted(seq.definitions))





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

 .. code-block:: none

    512 blocks, 0.71 s
    timing: True
    definitions: ['FOV', 'Matrix', 'Name', 'TotalDuration']




.. GENERATED FROM PYTHON SOURCE LINES 149-151

Sequence diagram
----------------

.. GENERATED FROM PYTHON SOURCE LINES 151-154

.. code-block:: Python


    seq.paper_plot(tr=32)




.. image-sg:: /generated/gallery/05-sequence-modules/images/sphx_glr_03_sequence_app_001.png
   :alt: 03 sequence app
   :srcset: /generated/gallery/05-sequence-modules/images/sphx_glr_03_sequence_app_001.png
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 155-160

Acquisition order
-----------------

``kernel`` writes a ``LIN`` label on every acquisition, so the order the loop
played is read back from the sequence rather than reconstructed.

.. GENERATED FROM PYTHON SOURCE LINES 160-163

.. code-block:: Python


    pp.plot.plot_kspace(seq, color_by="order", plane="xy", show_trajectory=False)




.. image-sg:: /generated/gallery/05-sequence-modules/images/sphx_glr_03_sequence_app_002.png
   :alt: 03 sequence app
   :srcset: /generated/gallery/05-sequence-modules/images/sphx_glr_03_sequence_app_002.png
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 164-170

Command-line interface
----------------------

``main`` is derived from the class, and its parameters are those of
``init_sequence``, so a subclass gets a command-line interface without
declaring one.

.. GENERATED FROM PYTHON SOURCE LINES 170-173

.. code-block:: Python


    main = SpoiledGradientEcho.main
    print(str(__import__("inspect").signature(main))[:120], "...")




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

 .. code-block:: none

    (plot: 'bool' = False, test_report: 'bool' = False, write_seq: 'bool' = False, seq_filename: 'str | None' = None, *, sys ...





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

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


.. _sphx_glr_download_generated_gallery_05-sequence-modules_03_sequence_app.py:

.. only:: html

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

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

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

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

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

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

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


.. only:: html

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

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