Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QDAC2: Generalise virtual sweep #155

Merged
merged 2 commits into from
Sep 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Large diffs are not rendered by default.

86 changes: 69 additions & 17 deletions qcodes_contrib_drivers/drivers/QDevil/QDAC2.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import Any, NewType, Sequence, List, Dict, Tuple, Optional
from packaging.version import parse

# Version 0.16.0
# Version 0.17.0
#
# Guiding principles for this driver for QDevil QDAC-II
# -----------------------------------------------------
Expand Down Expand Up @@ -1544,7 +1544,7 @@ def waveform(self, values: Sequence[float]) -> None:
self._parent.write_floats(f'trac:data "{self.name}",', values)


class Sweep_2D_Context:
class Virtual_Sweep_Context:
astafan8 marked this conversation as resolved.
Show resolved Hide resolved

def __init__(self, arrangement: 'Arrangement_Context', sweep: np.ndarray,
start_sweep_trigger: Optional[str], inner_step_time_s: float,
Expand Down Expand Up @@ -1679,8 +1679,24 @@ def initiate_correction(self, gate: str, factors: Sequence[float]):
self._correction[index] = factors

def set_virtual_voltage(self, gate: str, voltage: float) -> None:
index = self._gate_index(gate)
"""Set virtual voltage on specific gate

The actual voltage that the gate will receive depends on the
correction matrix.

Args:
gate (str): Name of gate
voltage (float): Voltage corresponding to no correction
"""
try:
index = self._gate_index(gate)
except KeyError:
raise ValueError(f'No gate named "{gate}"')
self._virtual_voltages[index] = voltage
actual_V = self.actual_voltages()[index]
channel_number = self._channels[index]
self._qdac.channel(channel_number).dc_constant_V(actual_V)


def add_correction(self, gate: str, factors: Sequence[float]) -> None:
"""Update how much a particular gate influences the other gates
Expand Down Expand Up @@ -1745,45 +1761,81 @@ def get_trigger_by_name(self, name: str) -> QDac2Trigger_Context:
print(f'Internal triggers: {list(self._internal_triggers.keys())}')
raise

def virtual_sweep(self, gate: str, voltages: Sequence[float],
start_sweep_trigger: Optional[str] = None,
step_time_s: float = 1e-5,
step_trigger: Optional[str] = None
) -> Virtual_Sweep_Context:
"""Sweep a gate to create a 1D sweep

Args:
gate (str): Name of sweeping gate
voltages (Sequence[float]): Virtual sweep voltages
outer_gate (str): Name of slow-changing (outer) gate
start_sweep_trigger (None, optional): Trigger that starts sweep
step_time_s (float, optional): Delay between voltage changes
step_trigger (None, optional): Trigger that marks each step

Returns:
Virtual_Sweep_Context: context manager
"""
sweep = self._calculate_1d_values(gate, voltages)
return Virtual_Sweep_Context(self, sweep, start_sweep_trigger,
step_time_s, step_trigger)

def _calculate_1d_values(self, gate: str, voltages: Sequence[float]
) -> np.ndarray:
original_voltage = self.virtual_voltage(gate)
index = self._gate_index(gate)
sweep = []
for v in voltages:
self._virtual_voltages[index] = v
sweep.append(self.actual_voltages())
self._virtual_voltages[index] = original_voltage
return np.array(sweep)


def virtual_sweep2d(self, inner_gate: str, inner_voltages: Sequence[float],
outer_gate: str, outer_voltages: Sequence[float],
start_sweep_trigger: Optional[str] = None,
inner_step_time_s: float = 1e-5,
inner_step_trigger: Optional[str] = None
) -> Sweep_2D_Context:
) -> Virtual_Sweep_Context:
"""Sweep two gates to create a 2D sweep

Args:
inner_gate (str): Name of fast-changing (inner) gate
inner_voltages (Sequence[float]): Inner gate voltages
inner_voltages (Sequence[float]): Inner gate virtual voltages
outer_gate (str): Name of slow-changing (outer) gate
outer_voltages (Sequence[float]): Outer gate voltages
outer_voltages (Sequence[float]): Outer gate virtual voltages
start_sweep_trigger (None, optional): Trigger that starts sweep
inner_step_time_s (float, optional): Delay between voltage changes
inner_step_trigger (None, optional): Trigger that marks each step

Returns:
Sweep_2D_Context: context manager
Virtual_Sweep_Context: context manager
"""
sweep = self._calculate_sweep_values(inner_gate, inner_voltages,
sweep = self._calculate_2d_values(inner_gate, inner_voltages,
outer_gate, outer_voltages)
return Sweep_2D_Context(self, sweep, start_sweep_trigger,
return Virtual_Sweep_Context(self, sweep, start_sweep_trigger,
inner_step_time_s, inner_step_trigger)

def _calculate_sweep_values(self, inner_gate: str,
inner_voltages: Sequence[float],
outer_gate: str,
outer_voltages: Sequence[float]) -> np.ndarray:
def _calculate_2d_values(self, inner_gate: str,
inner_voltages: Sequence[float],
outer_gate: str,
outer_voltages: Sequence[float]) -> np.ndarray:
original_fast_voltage = self.virtual_voltage(inner_gate)
original_slow_voltage = self.virtual_voltage(outer_gate)
outer_index = self._gate_index(outer_gate)
inner_index = self._gate_index(inner_gate)
sweep = []
for slow_V in outer_voltages:
self.set_virtual_voltage(outer_gate, slow_V)
self._virtual_voltages[outer_index] = slow_V
for fast_V in inner_voltages:
self.set_virtual_voltage(inner_gate, fast_V)
self._virtual_voltages[inner_index] = fast_V
sweep.append(self.actual_voltages())
self.set_virtual_voltage(inner_gate, original_fast_voltage)
self.set_virtual_voltage(outer_gate, original_slow_voltage)
self._virtual_voltages[inner_index] = original_fast_voltage
self._virtual_voltages[outer_index] = original_slow_voltage
return np.array(sweep)

def _gate_index(self, gate: str) -> int:
Expand Down
2 changes: 2 additions & 0 deletions qcodes_contrib_drivers/tests/QDevil/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Simulated instrument:
$ source venv/bin/activate
$ pytest qcodes_contrib_drivers/tests/QDevil/test_sim_qdac2_*.py

To silence warnings, use `-W ignore::DeprecationWarning`.

Real instrument:

$ source venv/bin/activate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,41 @@ def test_arrangement_default_correction(qdac): # noqa
np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]))


def test_arrangement_default_actuals(qdac): # noqa
def test_arrangement_set_virtual_voltage_non_exiting_gate(qdac): # noqa
arrangement = qdac.arrange(gates={'plunger': 1})
# -----------------------------------------------------------------------
with pytest.raises(ValueError) as error:
arrangement.set_virtual_voltage('sensor', 1.0)
# -----------------------------------------------------------------------
assert 'No gate named "sensor"' in repr(error)


def test_arrangement_set_virtual_voltage_effectuated_immediately(qdac): # noqa
arrangement = qdac.arrange(gates={'plunger1': 1, 'plunger2': 2, 'plunger3': 3})
qdac.start_recording_scpi()
# -----------------------------------------------------------------------
arrangement.set_virtual_voltage('plunger2', 0.5)
# -----------------------------------------------------------------------
commands = qdac.get_recorded_scpi_commands()
assert commands == ['sour2:volt:mode fix', 'sour2:volt 0.5']


def test_arrangement_default_actuals_1d(qdac): # noqa
arrangement = qdac.arrange(gates={'plunger1': 1, 'plunger2': 2})
arrangement.set_virtual_voltage('plunger2', 1.0)
# -----------------------------------------------------------------------
sweep = arrangement.virtual_sweep(
gate='plunger1',
voltages=np.linspace(-0.1, 0.1, 5),
step_time_s=2e-5)
# -----------------------------------------------------------------------
assert np.allclose(sweep.actual_values_V('plunger1'),
[-0.1, -0.05, 0.0, 0.05, 0.1])
assert np.allclose(sweep.actual_values_V('plunger2'),
[1.0, 1.0, 1.0, 1.0, 1.0])


def test_arrangement_default_actuals_2d(qdac): # noqa
arrangement = qdac.arrange(gates={'plunger1': 1, 'plunger2': 2, 'plunger3': 3})
# -----------------------------------------------------------------------
sweep = arrangement.virtual_sweep2d(
Expand Down Expand Up @@ -186,6 +220,14 @@ def test_stability_diagram_external(qdac): # noqa
assert commands == [
'outp:trig4:sour int1',
'outp:trig4:widt 1e-06',
'sour3:volt:mode fix',
'sour3:volt 0.1',
'sour6:volt:mode fix',
'sour6:volt 0.176',
'sour7:volt:mode fix',
'sour7:volt 0.383',
'sour8:volt:mode fix',
'sour8:volt 0.69693',
'sour3:dc:mark:sst 1',
# Sensor 1
'sour3:dc:trig:sour hold',
Expand Down