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 Phases and Chunking) as the base experiment, which are automatically called when the experiment is run.
An exception to this is the step() phase, as it must be called explicitly by the user, as shown in the example below.
Atomiq ExperimentComponents DBcomponent 0component 1component 2component 3component 4component 5component 6pre* and post* hookscomponents0 1
2 4argumentsAtomiq Block 0Nested Blocksuser methodspre* and post* hooks0 3
4 5componentsargumentsAtomiq Block 1user methodssteppre* and post* hookscomponentsAtomiq block 2pre* and post* hookscomponentsAtomiq block 3components1 3
5 6pre* and post* hooks
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 ComponentsDB 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 Phases and Chunking which are automatically executed. Blocks do not have a
step() method but can implement arbitrary user functions which are then called from within the 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.
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.
fromartiq.experimentimportkernel,ms,TBoolfromatomiqimportAtomiqExperiment,AtomiqBlockfrommy_blocksimportImagingclassMOT(AtomiqBlock):components=["ttl_cool"]@kerneldefstep(self,point,optional_argument:TBool=False):self.log.info("Test message from the MOT")ifoptional_argument:self.ttl_cool.pulse(3*ms)classATQExperiment(AtomiqExperiment):components=["lock_cooler","aom_cooler"]blocks=[MOT]@kerneldefstep(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)