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.

Example

In the following code the experiment with the name ATQExperiment wants to use the sequence defined in the MOT class. By stating blocks = [MOT] the ATQExperiment requires the MOT block. The step() function becomes available as self.MOT.step(), reflecting the name of the block. In addition to the required argument 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 optional_argument.

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)