.. _blocks: Blocks ====== In atomiq you can define so-called blocks which can be used as black boxes within other atomiq experiments or other atomiq blocks (you smell the recursion?). This allows you work out a part of your sequence (e.g. MOT-loading, transfer, imaging,...) once and then reuse it easily in all your other experiments. Since an atomiq block is also an atomiq experiment in itself, you can also run your block standalone. You define that your experiment relies on a certain block by adding it to the class-level blocks list of your experiment class. Then atomiq takes care for all the house-keeping (e.g. initialization of the needed components and recursive block initialization) of the block and import all the block members (functions and attributes you defined) to your experiment. Blocks can implement the same phases (see :ref:`phases_chunking`) as the base experiment, which are automatically called when the experiment is run. An exception to this is the :code:`step()` phase, as it must be called explicitly by the user, as shown in the example below. .. epigraph:: .. raw:: html :file: img/blocks.svg This schematic shows the basic ideas of blocks. Each block is a almost complete experiment by itself with its own components and arguments, which are initialized during the build process. Note, that you can use components from the :code:`Components DB` in multiple blocks and the main experiment. Atomiq then takes care that each component is only initialized once and properly shared. Each block can also have all the pre- and post-hooks described in :ref:`phases_chunking` which are automatically executed. Blocks do not have a :code:`step()` method but can implement arbitrary user functions which are then called from within the :code:`step()` method of the main experiment. It is also possible to nest blocks without loosing any functionality. Note that this is a schematic only. For example code see the example below. .. rubric:: Example In the following code the experiment with the name :code:`ATQExperiment` wants to use the sequence defined in the :code:`MOT` class. By stating :code:`blocks = [MOT]` the :code:`ATQExperiment` requires the :code:`MOT` block. The :code:`step()` function becomes available as :code:`self.MOT.step()`, reflecting the name of the block. In addition to the required argument :code:`point` it is also possible to specify further keyword arguments to be able to change the behavior of the block when calling it. In the example below this is done by the keyword argument :code:`optional_argument`. .. code-block:: python :emphasize-lines: 22,31 from artiq.experiment import kernel, ms, TBool from atomiq import AtomiqExperiment, AtomiqBlock from my_blocks import Imaging class MOT(AtomiqBlock): components = ["ttl_cool"] @kernel def step(self, point, optional_argument: TBool = False): self.log.info("Test message from the MOT") if optional_argument: self.ttl_cool.pulse(3*ms) class ATQExperiment(AtomiqExperiment): components = ["lock_cooler", "aom_cooler"] blocks = [MOT] @kernel def step(self, point): self.log.info("Test message from the core") delay(3*ms) self.aom_cooler.detune(3e6) self.MOT.step(point, optional_argument=True)