Source Measure Units (SMUs)#
SMUs are precision instruments that can source current or voltage, commonly supplying a constant amount of power to flux tunable qubits. The Keysight M9614A and M9615A are PXIe five-channel precision SMUs. SMU integrated with QCS can support sourcing current and voltage up to 30 V / 60 mA with resolution down to 6 μV / 100 pA for each channel.
Port 1 |
Port 2 |
Port 3 |
Port 4 |
Port 5 |
|
---|---|---|---|---|---|
Maximum Voltage / Maximum Current |
30V / 60mA |
30V / 60mA |
30V / 60mA |
30V / 60mA |
30V / 60mA |
Resolution Voltage / Resolution Current |
6µV / 100pA |
6µV / 100pA |
6µV / 100pA |
6µV / 100pA |
6µV / 100pA |
SMU can source precision voltage / current at each port on user request. Setting of voltage / current is done in steps (ramp) instead of one shot. This is to avoid sudden change in SMU port output which is expected to be connected to quantum circuits. By default, 100ms is the time taken for any ramp in voltage / current unless user manually requests for a faster ramp rate / slower ramp rate. There is also a limit set for faster ramp rate (minimum 1ms) / slower ramp rate (maximum 1 sec).
Ramping Modes:
Auto-Ramp Mode
Ramping is fixed at 100 ms duration with 100 steps for Voltage >= 600uV / Current >= 10nA
Each step is executed in 1 ms.
Step size depends on the requested target value.
If Voltage < 600uV / Current < 10nA, number of steps will be reduced with step value as minimum resolution value (6uV/100pA)
Manual-Ramp Mode
User specifies a manual ramp rate.
Ramp step duration: minimum 1 ms, maximum 1000 ms (1 sec).
Ramp steps: minimum 1, maximum 1000.
Ramp duration is auto adjusted to keep within 1 ms >= duration <= 1 sec.
This document will explain how to control SMU from the QCS SDK.
There are two categories of approaches that can be used to set the SMU voltage or current.
- Controlling SMUs directly without QCS program
One approach is to set SMU voltage or current directly through the
HclBackend
class. This does not require the submission of a program.The
set_smu_current
methodThis sets constant current on a single SMU port without ramping rate
backend = qcs.HclBackend(channel_mapper=mapper) address = qcs.Address(chassis= 1, slot= 12, channel= 1) backend.set_smu_current(address, constant_current=1e-6)
This sets constant current on a single SMU port with ramping rate
backend = qcs.HclBackend(channel_mapper=mapper) address = qcs.Address(chassis= 1, slot= 12, channel= 1) backend.set_smu_current(address, constant_current=1.5e-6, ramping_rate=2e-6) # Note: If user attempted total ramping time of an operation is more than one second, then the ramp rate will be determined by the backend.
It is possible to take in multiple addresses to set the currents simultaneously.
This sets constant current on multiple SMU ports without ramping rate
backend = qcs.HclBackend(channel_mapper=mapper) address_1 = qcs.Address(chassis= 1, slot= 12, channel= 1) address_2 = qcs.Address(chassis= 1, slot= 12, channel= 2) addresses = [address_1, address_2] backend.set_smu_current(addresses, constant_current=[1.1e-6, 1.2e-6])
This sets constant current on multiple SMU ports with ramping rate
backend = qcs.HclBackend(channel_mapper=mapper) address_1 = qcs.Address(chassis= 1, slot= 12, channel= 1) address_2 = qcs.Address(chassis= 1, slot= 12, channel= 2) addresses = [address_1, address_2] backend.set_smu_current(addresses, constant_current=[1e-6, 1.5e-6], ramping_rate=[2e-6,3e-6]) # Note: If user attempted total ramping time of an operation is more than one second, then the ramp rate will be determined by the backend. # Note: if multiple addresses are supplied as a list, then multiple ramping rate shall also be a list corresponding to each address.
The
get_smu_current
methodThis gets the active current on a single SMU port
backend = qcs.HclBackend(channel_mapper=mapper) address = qcs.Address(chassis= 1, slot= 12, channel= 1) current_value = backend.get_smu_current(address)
The
set_smu_voltage
methodThis sets constant voltage on a single SMU port without ramping rate
backend = qcs.HclBackend(channel_mapper=mapper) address = qcs.Address(chassis= 1, slot= 12, channel= 1) backend.set_smu_voltage(address, constant_voltage=1.0)
This sets constant voltage on a single SMU port with ramping rate
backend = qcs.HclBackend(channel_mapper=mapper) address = qcs.Address(chassis= 1, slot= 12, channel= 1) backend.set_smu_voltage(address, constant_voltage=2.0, ramping_rate=2.0) # Note: If user attempted total ramping time of an operation is more than one second, then the ramp rate will be determined by the backend.
Likewise this can also set voltages for multiple addresses at once.
This sets constant voltage on multiple SMU ports without ramping rate
backend = qcs.HclBackend(channel_mapper=mapper) address_1 = qcs.Address(chassis= 1, slot= 12, channel= 1) address_2 = qcs.Address(chassis= 1, slot= 12, channel= 2) addresses = [address_1, address_2] backend.set_smu_voltage(addresses, constant_voltage=[1.1, 1.2])
This sets constant voltage on multiple SMU ports with ramping rate
backend = qcs.HclBackend(channel_mapper=mapper) address_1 = qcs.Address(chassis= 1, slot= 12, channel= 1) address_2 = qcs.Address(chassis= 1, slot= 12, channel= 2) addresses = [address_1, address_2] backend.set_smu_voltage(addresses, constant_voltage=[2.77, 3.77], ramping_rate=[10, 5]) # Note: If user attempted total ramping time of an operation is more than one second, then the ramp rate will be determined by the backend.
The
get_smu_voltage
methodThis gets the active voltage oon a single SMU port
backend = qcs.HclBackend(channel_mapper=mapper) address = qcs.Address(chassis= 1, slot= 12, channel= 1) voltage_value = backend.get_smu_voltage(address)
The
calibrate_smu_channels
methodThis calibrates all SMU ports in the system.
Warning: The SMU will generate current during calibration. Disconnect all SMU outputs from your device before running this command.
backend = qcs.HclBackend(channel_mapper=mapper) backend.calibrate_smu_channels()
The
turn_off_smu
methodThis is to turn the SMU channel completely off
backend = qcs.HclBackend(channel_mapper=mapper) address = qcs.Address(chassis= 1, slot= 12, channel= 1) backend.turn_off_smu(address)
- Controlling SMUs using QCS Program.
The next approach is to set the SMU voltage or current as part of the execution a QCS Program. There are three placements for where the SMU operations can be specified to occur.
Before a program execution as Pre-Program Operations
After a program execution as Post-Program Operations
SW Sweep Program execution as Pre-Iterate Operations
First, let’s see how to set up SetCurrent and SetVoltage operations that will be part of the Program object.
The
SetCurrent
ClassUsed to set target current and ramping rate for SMU channels.
import keysight.qcs as qcs smu_channel = qcs.Channels(range(1), "smu_channel", True) const_current = qcs.Scalar("const_current", value=0.25e-6, dtype=float) ramp_rate = qcs.Scalar("ramp_rate", value=0.01, dtype=float) current_operation = qcs.SetCurrent( smu_channel, const_current, ramp_rate, "current_op" )
The above code is used to set the current on one SMU channel. The default value for ramping_rate is None, which lets the backend determine a ramping rate.
The
SetVoltage
ClassUsed to set target voltage and ramping rate for SMU channels.
import keysight.qcs as qcs smu_channels = qcs.Channels(range(2), "smu_channels", True) const_voltages = qcs.Array("const_voltages", value=[0.25, 0.26], dtype=float) ramp_rates = qcs.Array("ramp_rates", value=[1.0, 2.0], dtype=float) voltage_operation = qcs.SetVoltage( smu_channels, const_voltages, ramp_rates, "current_ops" )
The above code is used to set the voltage in two SMU channels. The default value for ramping_rate is None, which lets the backend determine a ramping rate. If the expected time of the operation is more than one second, then the ramp rate will be determined by the backend.
Now let’s look the three options for how these operations can be inserted into a program.
The
add_pre_program_operation
method (Pre-Program Operations)Pre-Program Operations run before program execution. They will only execute one time per program.
Pre-Program Operations for Current
import keysight.qcs as qcs control_channels = Channels(0, "xy_pulse") smu_channel = qcs.Channels(0, "smu_channel", True) backend = qcs.HclBackend(channel_mapper=mapper) program = qcs.Program() gauss = qcs.RFWaveform(duration=80e-9, envelope=qcs.GaussianEnvelope(num_sigma=4), amplitude=1, rf_frequency=5.1e9) program.add_waveform(gauss, control_channels) target_pre_current = qcs.Scalar("target_pre_current", value=1e-7, dtype=float) ramp_rate = qcs.Scalar("ramp_rate", value=1e-6, dtype=float) set_pre_current = qcs.SetCurrent( smu_channel, target_pre_current, ramp_rate ) program.add_pre_program_operation(set_pre_current) program.n_shots(1000) program = qcs.Executor(backend).execute(program)
Pre-Program Operations for Voltage
import keysight.qcs as qcs control_channels = Channels(0, "xy_pulse") smu_channel = qcs.Channels(0, "smu_channel", True) backend = qcs.HclBackend(channel_mapper=mapper) program = qcs.Program() gauss = qcs.RFWaveform(duration=80e-9, envelope=qcs.GaussianEnvelope(num_sigma=4), amplitude=1, rf_frequency=5.1e9) program.add_waveform(gauss, control_channels) target_pre_voltage = qcs.Scalar("target_pre_voltage", value=1.88, dtype=float) ramp_rate = qcs.Scalar("ramp_rate", value=2.0, dtype=float) set_pre_voltage = qcs.SetVoltage( smu_channel, target_pre_voltage, ramp_rate ) program.add_pre_program_operation(set_pre_voltage) program.n_shots(1000) program = qcs.Executor(backend).execute(program)
The
add_post_program_operation
method (Post-Program Operations)Post-Program Operations run after program execution.They will only execute one time per program.
Post-Program Operations for Current
import keysight.qcs as qcs control_channels = Channels(0, "xy_pulse") smu_channel = qcs.Channels(0, "smu_channel", True) backend = qcs.HclBackend(channel_mapper=mapper) program = qcs.Program() gauss = qcs.RFWaveform(duration=80e-9, envelope=qcs.GaussianEnvelope(num_sigma=4), amplitude=1, rf_frequency=5.1e9) program.add_waveform(gauss, control_channels) target_post_current = qcs.Scalar("target_post_current", value=-1e-7, dtype=float) ramp_rate = qcs.Scalar("ramp_rate", value=1e-6, dtype=float) set_post_current = qcs.SetCurrent( smu_channel, target_post_current, ramp_rate ) program.n_shots(1000) program.add_post_program_operation(set_post_current) program = qcs.Executor(backend).execute(program)
Post-Program Operations for Voltage
import keysight.qcs as qcs control_channels = Channels(0, "xy_pulse") smu_channel = qcs.Channels(0, "smu_channel", True) backend = qcs.HclBackend(channel_mapper=mapper) program = qcs.Program() gauss = qcs.RFWaveform(duration=80e-9, envelope=qcs.GaussianEnvelope(num_sigma=4), amplitude=1, rf_frequency=5.1e9) program.add_waveform(gauss, control_channels) target_post_voltage = qcs.Scalar("target_post_voltage", value=-3.66, dtype=float) ramp_rate = qcs.Scalar("ramp_rate", value=5.0, dtype=float) set_post_voltage = qcs.SetVoltage( smu_channel, target_post_voltage, ramp_rate ) program.n_shots(1000) program.add_post_program_operation(set_post_voltage) program = qcs.Executor(backend).execute(program)
The
sweep
method (Pre-Iterate Operations)These SMU operations run before a software-time sweep iteration. They will execute once for each sweep point.
Pre-Iterate Operations for Current
import keysight.qcs as qcs control_channels = Channels(0, "xy_pulse") smu_channel = qcs.Channels(0, "smu_channel", True) backend = qcs.HclBackend(channel_mapper=mapper) program = qcs.Program() gauss = qcs.RFWaveform(duration=80e-9, envelope=qcs.GaussianEnvelope(num_sigma=4), amplitude=1, rf_frequency=5.1e9) program.add_waveform(gauss, control_channels) target_current = qcs.Scalar("target_current", dtype=float) current_sweep = qcs.Array("current_sweep", value=[0.8e-7,0.5e-7,0.2e-7], dtype=float) ramp_rate = qcs.Scalar("ramp_rate", value=1e-6, dtype=float) set_current = qcs.SetCurrent( smu_channel, target_current, ramp_rate ) program.n_shots(1000) program.sweep(current_sweep, target_current, pre_iterate_operation_list=[set_current]) program = qcs.Executor(backend).execute(program) # Note: Sweep shall be SW Sweep for Voltage / Current. No HW Sweep possible for SMU.
Pre-Iterate Operations for Voltage
import keysight.qcs as qcs control_channels = Channels(0, "xy_pulse") smu_channel = qcs.Channels(0, "smu_channel", True) backend = qcs.HclBackend(channel_mapper=mapper) program = qcs.Program() gauss = qcs.RFWaveform(duration=80e-9, envelope=qcs.GaussianEnvelope(num_sigma=4), amplitude=1, rf_frequency=5.1e9) program.add_waveform(gauss, control_channels) target_voltage = qcs.Scalar("target_voltage", dtype=float) voltage_sweep = qcs.Array("voltage_sweep", value=[0.88,0.77,0.66, 0.55, 0.44], dtype=float) ramp_rate = qcs.Scalar("ramp_rate", value=1.0, dtype=float) set_voltage = qcs.SetVoltage( smu_channel, target_voltage, ramp_rate ) program.n_shots(1000) program.sweep(voltage_sweep, target_voltage, pre_iterate_operation_list=[set_voltage]) program = qcs.Executor(backend).execute(program) # Note: Sweep shall be SW Sweep for Voltage / Current. No HW Sweep possible for SMU.