Coils example¶
Scenario¶
We want to control three pairs of magnetic field coils in Helmholtz configuration. Each of the coil pairs is addressed by a single current supply controlled by an analog signal from a Zotino DAC channel.
This example makes use of the atomiq implementation to use parallel voltage ramps and set multiple channels on a Zotino dac within a parallel statement without timing collisions.
Experiment
from artiq.experiment import kernel, delay, ms, parallel
from atomiq import AtomiqExperiment
class ATQExperiment(AtomiqExperiment):
components = ["coils_x", "coils_y", "coils_z", "zotino0"]
arguments = {
"initial_x_field": {"default": 0.2, "unit": "G"},
"initial_y_field": {"default": 1.0, "unit": "G"},
"initial_z_field": {"default": 0.5, "unit": "G"},
"end_x_field": {"default": 0.6, "unit": "G"},
"end_y_field": {"default": 1.2, "unit": "G"},
"end_z_field": {"default": 0.2, "unit": "G"},
"field_ramp_time": {"default": 40, "unit": "ms"},
}
@kernel
def step(self, point):
with parallel:
# Set the field in each direction to an initial value inside a parallel
# statement. This uses the atomiq zotino scheduler to avoid collisions
# between the write operations as would occure here with standard ARTIQ.
self.coils_x.set_field(point.initial_x_field)
self.coils_y.set_field(point.initial_y_field)
self.coils_z.set_field(point.initial_z_field)
delay(point.loading_time)
with self.zotino0.parallel_arb:
self.coils_x.ramp_field(point.field_ramp_time, field_end = point.end_x_field)
self.coils_y.ramp_field(point.field_ramp_time, field_end = point.end_y_field)
delay(1*ms)
# The coil_z ramp starts 1ms later than the coils_x and coils_y ramps.
self.coils_z.ramp_field(point.field_ramp_time, field_end = point.end_z_field)
@kernel
def poststep(self, point):
# Set the coil currents to zero again after the experiment step
self.coils_offset_x.set_field(0.)
self.coils_offset_y.set_field(0.)
self.coils_offset_z.set_field(0.)
Components
# Basics
components = {
"log": {
"classname": "atomiq.components.basics.log.KernelLogger"
}
}
components["zotino0"] = {
"classname": "atomiq.components.sinara.dac.Zotino",
"arguments": {"zotino_device": "@zotino0", "max_parallel_ramps":3, "max_parallel_ramp_steps": 3000},
}
# Offset X coils
components.update(
{
# Zotino channel which controls the current source
"vsource_coils_x": {
"classname": "atomiq.components.sinara.dac.ZotinoChannel",
"arguments": {"dac_device": "&zotino0", "channel": 1},
},
# Translation of the analog control signal to the current output of the current supply.
# Here, an applied voltage of 1V corresponds to 1A current
"cal_isource_coils_x": {
"classname": "atomiq.components.basics.calibration.LinearCalibration",
"arguments": {"a": 1.0, "b": 0.0, "input_unit": "V", "output_unit": "A"},
},
# Definition of the current source driving the coils
"isource_coils_x": {
"classname": "atomiq.components.electronics.currentsource.VoltageControlledCurrentSource",
"arguments": {
"voltage_source": "&vsource_coils_x",
"calibration": "&cal_isource_coils_x",
"min_current": -8.0,
"max_current": 8.0,
"default_ramp_steps": 40,
},
},
# Translation from applied current to the generated magnetic field at the point of interest
# Here, 1A creates a field of 1.1G with an offset of 0G
"cal_field_coils_x": {
"classname": "atomiq.components.basics.calibration.LinearCalibration",
"arguments": {"a": 1/1.1, "b": 0.0, "input_unit": "I", "output_unit": "G"},
},
# Definition of the coil pair. Both coils are driven by the
"coils_x": {
"classname": "atomiq.components.coil.CoilPair",
"arguments": {
"current_source_coil1": "&isource_coils_x",
"current_source_coil2": "&isource_coils_x",
"calibration_field": "&cal_field_coils_x",
"calibration_gradient": "&cal_gradient_coils_x",
"switch": "&dummy_switch",
},
},
# We can't set a gradient with two coils in series in Helmholtz configuration
"cal_gradient_coils_x": {
"classname": "atomiq.components.basics.calibration.DummyCalibration",
"arguments": {},
},
# The coils are not switched by an additional fast mosfet switch or similar, use a dummy here
"dummy_switch": {"classname":"atomiq.components.dummies.DummySwitch", "arguments":{"channels":1}}
}
)
# Offset Y coils
# Details on sub components see above
components.update(
{
"vsource_coils_y": {
"classname": "atomiq.components.sinara.dac.ZotinoChannel",
"arguments": {"dac_device": "&zotino0", "channel": 2},
},
"cal_isource_coils_y": {
"classname": "atomiq.components.basics.calibration.LinearCalibration",
"arguments": {"a": 1.0, "b": 0.0, "input_unit": "V", "output_unit": "A"},
},
"isource_coils_y": {
"classname": "atomiq.components.electronics.currentsource.VoltageControlledCurrentSource",
"arguments": {
"voltage_source": "&vsource_coils_y",
"calibration": "&cal_isource_coils_y",
"min_current": -9.0,
"max_current": 9.0,
"default_ramp_steps": 40,
},
},
"cal_field_coils_y": {
"classname": "atomiq.components.basics.calibration.LinearCalibration",
"arguments": {"a": 1/1.3, "b": 0.0, "input_unit": "I", "output_unit": "G"},
},
"coils_y": {
"classname": "atomiq.components.coil.CoilPair",
"arguments": {
"current_source_coil1": "&isource_coils_y",
"current_source_coil2": "&isource_coils_y",
"calibration_field": "&cal_field_coils_y",
"calibration_gradient": "&cal_gradient",
"switch": "&dummy_switch",
},
},
}
)
# Offset Z coils
# Details on sub components see above
components.update(
{
"vsource_coils_z": {
"classname": "atomiq.components.sinara.dac.ZotinoChannel",
"arguments": {"dac_device": "&zotino0", "channel": 3},
},
"cal_isource_coils_z": {
"classname": "atomiq.components.basics.calibration.LinearCalibration",
"arguments": {"a": 1.0, "b": 0.0, "input_unit": "V", "output_unit": "A"},
},
"isource_coils_z": {
"classname": "atomiq.components.electronics.currentsource.VoltageControlledCurrentSource",
"arguments": {
"voltage_source": "&vsource_coils_z",
"calibration": "&cal_isource_coils_z",
"min_current": -8.0,
"max_current": 8.0,
"default_ramp_steps": 40,
},
},
"cal_field_coils_z": {
"classname": "atomiq.components.basics.calibration.LinearCalibration",
"arguments": {"a": 1/3.4, "b": 0.0, "input_unit": "I", "output_unit": "G"},
},
"coils_z": {
"classname": "atomiq.components.coil.CoilPair",
"arguments": {
"current_source_coil1": "&isource_coils_z",
"current_source_coil2": "&isource_coils_z",
"calibration_field": "&cal_field_coils_z",
"calibration_gradient": "&cal_gradient",
"switch": "&dummy_switch",
},
},
}
)