From 8dcd867bf967de2a071b7520ac222eba051a9b76 Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Tue, 24 Jun 2025 13:58:41 +0200 Subject: [PATCH 01/20] First draft of qcodes wrapper --- src/qumada/utils/liveplot.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/qumada/utils/liveplot.py diff --git a/src/qumada/utils/liveplot.py b/src/qumada/utils/liveplot.py new file mode 100644 index 00000000..01acd702 --- /dev/null +++ b/src/qumada/utils/liveplot.py @@ -0,0 +1,32 @@ +import contextlib +from typing import Sequence + +from qcodes import Measurement +from qcodes.parameters import ParameterBase + + +class MeasurementAndPlot: + def __init__(self, *, name: str): + self.qcodes_measurement = Measurement(name=name) + + def register_parameter( + self, + parameter: ParameterBase, + setpoints: Sequence[str | ParameterBase] | None = None, + **kwargs): + self.qcodes_measurement.register_parameter(parameter, setpoints, **kwargs) + + @contextlib.contextmanager + def run(self): + with self.qcodes_measurement.run() as qcodes_datasaver: + yield DataSaverAndPlotter(self, qcodes_datasaver) + + +class DataSaverAndPlotter: + def __init__(self, parent: MeasurementAndPlot, qcodes_datasaver): + self._parent = parent + self.qcodes_datasaver = qcodes_datasaver + + def add_result(self, *args): + self.qcodes_datasaver.add_result(*args) + From 45fdd01459192565fb3be9622a8a88d2ab2f4ebb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 12:04:40 +0000 Subject: [PATCH 02/20] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qumada/utils/liveplot.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/qumada/utils/liveplot.py b/src/qumada/utils/liveplot.py index 01acd702..a4361155 100644 --- a/src/qumada/utils/liveplot.py +++ b/src/qumada/utils/liveplot.py @@ -1,5 +1,5 @@ import contextlib -from typing import Sequence +from collections.abc import Sequence from qcodes import Measurement from qcodes.parameters import ParameterBase @@ -10,10 +10,8 @@ def __init__(self, *, name: str): self.qcodes_measurement = Measurement(name=name) def register_parameter( - self, - parameter: ParameterBase, - setpoints: Sequence[str | ParameterBase] | None = None, - **kwargs): + self, parameter: ParameterBase, setpoints: Sequence[str | ParameterBase] | None = None, **kwargs + ): self.qcodes_measurement.register_parameter(parameter, setpoints, **kwargs) @contextlib.contextmanager @@ -29,4 +27,3 @@ def __init__(self, parent: MeasurementAndPlot, qcodes_datasaver): def add_result(self, *args): self.qcodes_datasaver.add_result(*args) - From 5c0beec711591010c8479f133e2a8565cacc0a03 Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Tue, 24 Jun 2025 15:42:21 +0200 Subject: [PATCH 03/20] Add failing measurement test --- src/tests/measurement_test.py | 126 ++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/tests/measurement_test.py diff --git a/src/tests/measurement_test.py b/src/tests/measurement_test.py new file mode 100644 index 00000000..cae11d5e --- /dev/null +++ b/src/tests/measurement_test.py @@ -0,0 +1,126 @@ +import dataclasses +import tempfile + +import pytest + +import threading + +import numpy as np +import yaml +from qcodes.dataset import ( + Measurement, + experiments, + initialise_or_create_database_at, + load_by_run_spec, + load_or_create_experiment, +) +from qcodes.station import Station + +from qumada.instrument.buffered_instruments import BufferedDummyDMM as DummyDmm +from qumada.instrument.buffers.buffer import ( + load_trigger_mapping, + map_triggers, + save_trigger_mapping, +) +from qumada.instrument.custom_drivers.Dummies.dummy_dac import DummyDac +from qumada.instrument.mapping import ( + DUMMY_DMM_MAPPING, + add_mapping_to_instrument, + map_terminals_gui, +) +from qumada.instrument.mapping.Dummies.DummyDac import DummyDacMapping +from qumada.measurement.scripts import ( + Generic_1D_parallel_asymm_Sweep, + Generic_1D_parallel_Sweep, + Generic_1D_Sweep, + Generic_1D_Sweep_buffered, + Generic_2D_Sweep_buffered, + Generic_nD_Sweep, + Timetrace, +) +from qumada.utils.generate_sweeps import generate_sweep, replace_parameter_settings +from qumada.utils.GUI import open_web_gui +from qumada.utils.load_from_sqlite_db import load_db +from qumada.utils.ramp_parameter import * + + +@dataclasses.dataclass +class MeasurementTestData: + trigger: threading.Event + + station: Station + dmm: DummyDmm + dac: DummyDac + + +@pytest.fixture +def measurement_test_data(): + trigger = threading.Event() + + # Setup qcodes station + station = Station() + + # The dummy instruments have a trigger_event attribute as replacement for + # the trigger inputs of real instruments. + + dmm = DummyDmm("dmm", trigger_event=trigger) + add_mapping_to_instrument(dmm, mapping=DUMMY_DMM_MAPPING) + station.add_component(dmm) + + dac = DummyDac("dac", trigger_event=trigger) + add_mapping_to_instrument(dac, mapping=DummyDacMapping()) + station.add_component(dac) + + + yield MeasurementTestData(trigger, station, dmm, dac) + station.close_all_registered_instruments() + +@pytest.fixture +def buffer_settings(): + return { + "sampling_rate": 512, + "duration": 1e-3, + "burst_duration": 1e-3, + "delay": 0, + } + +@pytest.fixture +def parameters(): + return { + "ohmic": { + "voltage": {"type": "gettable"}, + "current": {"type": "gettable"}, + }, + "gate1": {"voltage": {"type": "dynamic", "setpoints": np.linspace(0, np.pi, 7), "value": 0}}, + "gate2": {"voltage": {"type": "dynamic", "setpoints": np.linspace(0, np.pi, 12), "value": 0}}, + } + +@pytest.fixture +def db(): + with tempfile.TemporaryDirectory() as tmpdir: + db_path = tmpdir + "test.db" + load_db(db_path) + load_or_create_experiment("test", "dummy_sample") + yield db_path + +def test_1d_buffered(measurement_test_data, buffer_settings, parameters, db): + script = Generic_1D_Sweep_buffered() + script.setup( + parameters, + metadata=None, + buffer_settings=buffer_settings, + trigger_type="hardware", + trigger_start=measurement_test_data.trigger.set, + trigger_reset=measurement_test_data.trigger.clear, + ) + + mapping = { + 'ohmic': { + 'voltage': measurement_test_data.dmm.voltage, + 'current': measurement_test_data.dmm.current, + }, + 'gate1': {'voltage': measurement_test_data.dac.ch01.voltage,}, + 'gate2': {'voltage': measurement_test_data.dac.ch01.voltage,}, + } + script.gate_parameters = mapping + tmp = script.run() From 88df8b05be54e7ecd3ae623954bc04fac82faa59 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 13:43:22 +0000 Subject: [PATCH 04/20] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/tests/measurement_test.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/tests/measurement_test.py b/src/tests/measurement_test.py index cae11d5e..9dc55466 100644 --- a/src/tests/measurement_test.py +++ b/src/tests/measurement_test.py @@ -1,11 +1,9 @@ import dataclasses import tempfile - -import pytest - import threading import numpy as np +import pytest import yaml from qcodes.dataset import ( Measurement, @@ -71,10 +69,10 @@ def measurement_test_data(): add_mapping_to_instrument(dac, mapping=DummyDacMapping()) station.add_component(dac) - yield MeasurementTestData(trigger, station, dmm, dac) station.close_all_registered_instruments() + @pytest.fixture def buffer_settings(): return { @@ -84,6 +82,7 @@ def buffer_settings(): "delay": 0, } + @pytest.fixture def parameters(): return { @@ -95,6 +94,7 @@ def parameters(): "gate2": {"voltage": {"type": "dynamic", "setpoints": np.linspace(0, np.pi, 12), "value": 0}}, } + @pytest.fixture def db(): with tempfile.TemporaryDirectory() as tmpdir: @@ -103,6 +103,7 @@ def db(): load_or_create_experiment("test", "dummy_sample") yield db_path + def test_1d_buffered(measurement_test_data, buffer_settings, parameters, db): script = Generic_1D_Sweep_buffered() script.setup( @@ -115,12 +116,16 @@ def test_1d_buffered(measurement_test_data, buffer_settings, parameters, db): ) mapping = { - 'ohmic': { - 'voltage': measurement_test_data.dmm.voltage, - 'current': measurement_test_data.dmm.current, + "ohmic": { + "voltage": measurement_test_data.dmm.voltage, + "current": measurement_test_data.dmm.current, + }, + "gate1": { + "voltage": measurement_test_data.dac.ch01.voltage, + }, + "gate2": { + "voltage": measurement_test_data.dac.ch01.voltage, }, - 'gate1': {'voltage': measurement_test_data.dac.ch01.voltage,}, - 'gate2': {'voltage': measurement_test_data.dac.ch01.voltage,}, } script.gate_parameters = mapping tmp = script.run() From daee0160709da5265ad15cf2591d3dd8a311c5bc Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Wed, 25 Jun 2025 10:23:02 +0200 Subject: [PATCH 05/20] Fix measurement test impl --- src/tests/measurement_test.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/tests/measurement_test.py b/src/tests/measurement_test.py index 9dc55466..a47ac3d9 100644 --- a/src/tests/measurement_test.py +++ b/src/tests/measurement_test.py @@ -77,8 +77,8 @@ def measurement_test_data(): def buffer_settings(): return { "sampling_rate": 512, - "duration": 1e-3, - "burst_duration": 1e-3, + "duration": 12 / 512, + "burst_duration": 12 / 512, "delay": 0, } @@ -90,7 +90,7 @@ def parameters(): "voltage": {"type": "gettable"}, "current": {"type": "gettable"}, }, - "gate1": {"voltage": {"type": "dynamic", "setpoints": np.linspace(0, np.pi, 7), "value": 0}}, + "gate1": {"voltage": {"type": "dynamic", "setpoints": np.linspace(0, np.pi, 12), "value": 0}}, "gate2": {"voltage": {"type": "dynamic", "setpoints": np.linspace(0, np.pi, 12), "value": 0}}, } @@ -128,4 +128,15 @@ def test_1d_buffered(measurement_test_data, buffer_settings, parameters, db): }, } script.gate_parameters = mapping - tmp = script.run() + ds1, ds2 = script.run() + ds1 = ds1.to_xarray_dataset() + ds2 = ds2.to_xarray_dataset() + + np.testing.assert_almost_equal( + parameters["gate1"]["voltage"]["setpoints"], + ds1.dac_ch01_voltage.values, + ) + np.testing.assert_almost_equal( + parameters["gate2"]["voltage"]["setpoints"], + ds2.dac_ch01_voltage.values, + ) From 78fda1a5ef82c268942406db663258634d598b5f Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Wed, 25 Jun 2025 10:33:43 +0200 Subject: [PATCH 06/20] Move test setup to conftest file --- src/tests/conftest.py | 44 +++++++++++++++++++++++++++++++++++ src/tests/measurement_test.py | 42 ++++++--------------------------- 2 files changed, 51 insertions(+), 35 deletions(-) create mode 100644 src/tests/conftest.py diff --git a/src/tests/conftest.py b/src/tests/conftest.py new file mode 100644 index 00000000..76e99859 --- /dev/null +++ b/src/tests/conftest.py @@ -0,0 +1,44 @@ +import dataclasses +import threading + +import pytest +from qcodes.station import Station + +from qumada.instrument.buffered_instruments import BufferedDummyDMM as DummyDmm +from qumada.instrument.custom_drivers.Dummies.dummy_dac import DummyDac +from qumada.instrument.mapping import ( + DUMMY_DMM_MAPPING, + add_mapping_to_instrument, +) +from qumada.instrument.mapping.Dummies.DummyDac import DummyDacMapping + + +@dataclasses.dataclass +class MeasurementTestSetup: + trigger: threading.Event + + station: Station + dmm: DummyDmm + dac: DummyDac + + +@pytest.fixture +def measurement_test_setup(): + trigger = threading.Event() + + # Setup qcodes station + station = Station() + + # The dummy instruments have a trigger_event attribute as replacement for + # the trigger inputs of real instruments. + + dmm = DummyDmm("dmm", trigger_event=trigger) + add_mapping_to_instrument(dmm, mapping=DUMMY_DMM_MAPPING) + station.add_component(dmm) + + dac = DummyDac("dac", trigger_event=trigger) + add_mapping_to_instrument(dac, mapping=DummyDacMapping()) + station.add_component(dac) + + yield MeasurementTestSetup(trigger, station, dmm, dac) + station.close_all_registered_instruments() \ No newline at end of file diff --git a/src/tests/measurement_test.py b/src/tests/measurement_test.py index a47ac3d9..7e357040 100644 --- a/src/tests/measurement_test.py +++ b/src/tests/measurement_test.py @@ -42,35 +42,7 @@ from qumada.utils.ramp_parameter import * -@dataclasses.dataclass -class MeasurementTestData: - trigger: threading.Event - station: Station - dmm: DummyDmm - dac: DummyDac - - -@pytest.fixture -def measurement_test_data(): - trigger = threading.Event() - - # Setup qcodes station - station = Station() - - # The dummy instruments have a trigger_event attribute as replacement for - # the trigger inputs of real instruments. - - dmm = DummyDmm("dmm", trigger_event=trigger) - add_mapping_to_instrument(dmm, mapping=DUMMY_DMM_MAPPING) - station.add_component(dmm) - - dac = DummyDac("dac", trigger_event=trigger) - add_mapping_to_instrument(dac, mapping=DummyDacMapping()) - station.add_component(dac) - - yield MeasurementTestData(trigger, station, dmm, dac) - station.close_all_registered_instruments() @pytest.fixture @@ -104,27 +76,27 @@ def db(): yield db_path -def test_1d_buffered(measurement_test_data, buffer_settings, parameters, db): +def test_1d_buffered(measurement_test_setup, buffer_settings, parameters, db): script = Generic_1D_Sweep_buffered() script.setup( parameters, metadata=None, buffer_settings=buffer_settings, trigger_type="hardware", - trigger_start=measurement_test_data.trigger.set, - trigger_reset=measurement_test_data.trigger.clear, + trigger_start=measurement_test_setup.trigger.set, + trigger_reset=measurement_test_setup.trigger.clear, ) mapping = { "ohmic": { - "voltage": measurement_test_data.dmm.voltage, - "current": measurement_test_data.dmm.current, + "voltage": measurement_test_setup.dmm.voltage, + "current": measurement_test_setup.dmm.current, }, "gate1": { - "voltage": measurement_test_data.dac.ch01.voltage, + "voltage": measurement_test_setup.dac.ch01.voltage, }, "gate2": { - "voltage": measurement_test_data.dac.ch01.voltage, + "voltage": measurement_test_setup.dac.ch01.voltage, }, } script.gate_parameters = mapping From 79f220d7f036288b39a7c83e9020b34f98949aa4 Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Wed, 25 Jun 2025 11:34:33 +0200 Subject: [PATCH 07/20] Improve measurement code testing and add qumada device test --- src/tests/conftest.py | 17 ++++-- src/tests/device_test.py | 99 +++++++++++++++++++++++++++++++++++ src/tests/measurement_test.py | 12 ++--- 3 files changed, 116 insertions(+), 12 deletions(-) create mode 100644 src/tests/device_test.py diff --git a/src/tests/conftest.py b/src/tests/conftest.py index 76e99859..4fadaeff 100644 --- a/src/tests/conftest.py +++ b/src/tests/conftest.py @@ -1,8 +1,13 @@ import dataclasses import threading +import tempfile +import pathlib +import time import pytest from qcodes.station import Station +from qumada.utils.load_from_sqlite_db import load_db +from qcodes.dataset.experiment_container import load_or_create_experiment from qumada.instrument.buffered_instruments import BufferedDummyDMM as DummyDmm from qumada.instrument.custom_drivers.Dummies.dummy_dac import DummyDac @@ -21,9 +26,11 @@ class MeasurementTestSetup: dmm: DummyDmm dac: DummyDac + db_path: pathlib.Path + @pytest.fixture -def measurement_test_setup(): +def measurement_test_setup(tmp_path): trigger = threading.Event() # Setup qcodes station @@ -40,5 +47,9 @@ def measurement_test_setup(): add_mapping_to_instrument(dac, mapping=DummyDacMapping()) station.add_component(dac) - yield MeasurementTestSetup(trigger, station, dmm, dac) - station.close_all_registered_instruments() \ No newline at end of file + db_path = tmp_path / "test.db" + load_db(str(db_path)) + load_or_create_experiment("test", "dummy_sample") + + yield MeasurementTestSetup(trigger, station, dmm, dac, db_path) + station.close_all_registered_instruments() diff --git a/src/tests/device_test.py b/src/tests/device_test.py new file mode 100644 index 00000000..4fd278d2 --- /dev/null +++ b/src/tests/device_test.py @@ -0,0 +1,99 @@ +import dataclasses + +import pytest +import numpy as np + +from qumada.measurement.device_object import QumadaDevice + +from .conftest import MeasurementTestSetup + + +@dataclasses.dataclass +class DeviceTestSetup: + measurement_test_setup: MeasurementTestSetup + device: QumadaDevice + parameters: dict + namespace: dict + + +@pytest.fixture +def device_test_setup(measurement_test_setup): + """This fixture is derived from device_object_example""" + + parameters = { + "ohmic": { + "voltage": {"type": "gettable"}, + "current": {"type": "gettable"}, + }, + "gate1": {"voltage": {"type": "static"}}, + "gate2": {"voltage": {"type": "static"}}, + } + namespace = {} + device = QumadaDevice.create_from_dict(parameters, station=measurement_test_setup.station, namespace=namespace) + + buffer_settings = { + "sampling_rate": 512, + "num_points": 12, + "delay": 0, + } + + mapping = { + "ohmic": { + "voltage": measurement_test_setup.dmm.voltage, + "current": measurement_test_setup.dmm.current, + }, + "gate1": { + "voltage": measurement_test_setup.dac.ch01.voltage, + }, + "gate2": { + "voltage": measurement_test_setup.dac.ch02.voltage, + }, + } + + # This tells a measurement script how to start a buffered measurement. + # "Hardware" means that you want to use a hardware trigger. To start a measurement, + # the method provided as "trigger_start" is called. The "trigger_reset" method is called + # at the end of each buffered line, in our case resetting the trigger flag. + # For real instruments, you might have to define a method that sets the output of your instrument + # to a desired value as "trigger_start". For details on other ways to setup your triggers, + # check the documentation. + + buffer_script_settings = { + "trigger_type": "hardware", + "trigger_start": measurement_test_setup.trigger.set, + "trigger_reset": measurement_test_setup.trigger.clear, + } + + device.buffer_script_setup = buffer_script_settings + device.buffer_settings = buffer_settings + + # device.mapping() + # - map_terminals_gui(self.station.components, self.instrument_parameters, instrument_parameters) + device.instrument_parameters = mapping + # - self.update_terminal_parameters() + device.update_terminal_parameters() + + # map_triggers(station.components) ??? + measurement_test_setup.dac._qumada_mapping.trigger_in = None + measurement_test_setup.dmm._qumada_buffer.trigger, = measurement_test_setup.dmm._qumada_buffer.AVAILABLE_TRIGGERS + + return DeviceTestSetup( + measurement_test_setup, + device, + parameters, + namespace, + ) + + +def test_measured_ramp(device_test_setup): + gate1 = device_test_setup.namespace["gate1"] + + qcodes_data, = gate1.voltage.measured_ramp(0.4, start=-0.3, buffered=True) + assert gate1.voltage() == pytest.approx(0.4, abs=0.001) + + xarr = qcodes_data.to_xarray_dataset() + + set_points = xarr.dac_ch01_voltage.values + + expected = np.linspace(-0.3, 0.4, len(set_points)) + np.testing.assert_almost_equal(expected, set_points) diff --git a/src/tests/measurement_test.py b/src/tests/measurement_test.py index 7e357040..e70369f7 100644 --- a/src/tests/measurement_test.py +++ b/src/tests/measurement_test.py @@ -67,16 +67,10 @@ def parameters(): } -@pytest.fixture -def db(): - with tempfile.TemporaryDirectory() as tmpdir: - db_path = tmpdir + "test.db" - load_db(db_path) - load_or_create_experiment("test", "dummy_sample") - yield db_path -def test_1d_buffered(measurement_test_setup, buffer_settings, parameters, db): + +def test_1d_buffered(measurement_test_setup, buffer_settings, parameters): script = Generic_1D_Sweep_buffered() script.setup( parameters, @@ -96,7 +90,7 @@ def test_1d_buffered(measurement_test_setup, buffer_settings, parameters, db): "voltage": measurement_test_setup.dac.ch01.voltage, }, "gate2": { - "voltage": measurement_test_setup.dac.ch01.voltage, + "voltage": measurement_test_setup.dac.ch02.voltage, }, } script.gate_parameters = mapping From b2d248cf55db767cd579b49bc052aa639b3de9b8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 25 Jun 2025 09:34:46 +0000 Subject: [PATCH 08/20] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/tests/conftest.py | 8 ++++---- src/tests/device_test.py | 6 +++--- src/tests/measurement_test.py | 6 ------ 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/tests/conftest.py b/src/tests/conftest.py index 4fadaeff..c3b52717 100644 --- a/src/tests/conftest.py +++ b/src/tests/conftest.py @@ -1,13 +1,12 @@ import dataclasses -import threading -import tempfile import pathlib +import tempfile +import threading import time import pytest -from qcodes.station import Station -from qumada.utils.load_from_sqlite_db import load_db from qcodes.dataset.experiment_container import load_or_create_experiment +from qcodes.station import Station from qumada.instrument.buffered_instruments import BufferedDummyDMM as DummyDmm from qumada.instrument.custom_drivers.Dummies.dummy_dac import DummyDac @@ -16,6 +15,7 @@ add_mapping_to_instrument, ) from qumada.instrument.mapping.Dummies.DummyDac import DummyDacMapping +from qumada.utils.load_from_sqlite_db import load_db @dataclasses.dataclass diff --git a/src/tests/device_test.py b/src/tests/device_test.py index 4fd278d2..ab9fee74 100644 --- a/src/tests/device_test.py +++ b/src/tests/device_test.py @@ -1,7 +1,7 @@ import dataclasses -import pytest import numpy as np +import pytest from qumada.measurement.device_object import QumadaDevice @@ -75,7 +75,7 @@ def device_test_setup(measurement_test_setup): # map_triggers(station.components) ??? measurement_test_setup.dac._qumada_mapping.trigger_in = None - measurement_test_setup.dmm._qumada_buffer.trigger, = measurement_test_setup.dmm._qumada_buffer.AVAILABLE_TRIGGERS + (measurement_test_setup.dmm._qumada_buffer.trigger,) = measurement_test_setup.dmm._qumada_buffer.AVAILABLE_TRIGGERS return DeviceTestSetup( measurement_test_setup, @@ -88,7 +88,7 @@ def device_test_setup(measurement_test_setup): def test_measured_ramp(device_test_setup): gate1 = device_test_setup.namespace["gate1"] - qcodes_data, = gate1.voltage.measured_ramp(0.4, start=-0.3, buffered=True) + (qcodes_data,) = gate1.voltage.measured_ramp(0.4, start=-0.3, buffered=True) assert gate1.voltage() == pytest.approx(0.4, abs=0.001) xarr = qcodes_data.to_xarray_dataset() diff --git a/src/tests/measurement_test.py b/src/tests/measurement_test.py index e70369f7..249b9f50 100644 --- a/src/tests/measurement_test.py +++ b/src/tests/measurement_test.py @@ -42,9 +42,6 @@ from qumada.utils.ramp_parameter import * - - - @pytest.fixture def buffer_settings(): return { @@ -67,9 +64,6 @@ def parameters(): } - - - def test_1d_buffered(measurement_test_setup, buffer_settings, parameters): script = Generic_1D_Sweep_buffered() script.setup( From 06c8bb6a5d322fd890671ee2a7409d0aaf5e51cc Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Wed, 25 Jun 2025 16:54:59 +0200 Subject: [PATCH 09/20] Properly isolate test's global instrument state --- src/tests/mapping_test.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/tests/mapping_test.py b/src/tests/mapping_test.py index 2db714d5..14274ff8 100644 --- a/src/tests/mapping_test.py +++ b/src/tests/mapping_test.py @@ -52,34 +52,38 @@ from qumada.measurement.scripts.generic_measurement import Generic_1D_Sweep -@pytest.fixture(name="dmm", scope="session") +@pytest.fixture(name="dmm") def fixture_dmm(): dmm = DummyDmm("dmm") add_mapping_to_instrument(dmm, mapping=mapping.DUMMY_DMM_MAPPING) - return dmm + yield dmm + dmm.close() -@pytest.fixture(name="dac", scope="session") +@pytest.fixture(name="dac") def fixture_dac(): dac = DummyDac("dac") add_mapping_to_instrument(dac, mapping=DummyDacMapping()) - return dac + yield dac + dac.close() -@pytest.fixture(name="dci", scope="session") +@pytest.fixture(name="dci") def fixture_dci(): dci = DummyChannelInstrument("dci") add_mapping_to_instrument(dci, mapping=mapping.DUMMY_CHANNEL_MAPPING) - return dci + yield dci + dci.close() -@pytest.fixture(name="station_with_instruments", scope="session") +@pytest.fixture(name="station_with_instruments") def fixture_station_with_instruments(dmm, dac, dci): station = Station() station.add_component(dmm) station.add_component(dac) station.add_component(dci) - return station + yield station + station.close_all_registered_instruments() @pytest.fixture(name="script") From db63953a322f6df700b7905e237b2723734d0de5 Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Wed, 25 Jun 2025 16:55:14 +0200 Subject: [PATCH 10/20] Fix typo in test --- src/tests/measurement_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/measurement_test.py b/src/tests/measurement_test.py index e70369f7..0a0a50a8 100644 --- a/src/tests/measurement_test.py +++ b/src/tests/measurement_test.py @@ -104,5 +104,5 @@ def test_1d_buffered(measurement_test_setup, buffer_settings, parameters): ) np.testing.assert_almost_equal( parameters["gate2"]["voltage"]["setpoints"], - ds2.dac_ch01_voltage.values, + ds2.dac_ch02_voltage.values, ) From 7f3d4db0ebf4a641cc340a54f8b0e1b5d09a80f0 Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Wed, 25 Jun 2025 17:24:54 +0200 Subject: [PATCH 11/20] Inject live plotter into measurement script --- src/qumada/measurement/measurement.py | 7 ++++++ .../scripts/generic_measurement.py | 19 +++++++-------- src/qumada/utils/liveplot.py | 24 ++++++++++++++++--- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/qumada/measurement/measurement.py b/src/qumada/measurement/measurement.py index 293a4ec6..2849624d 100644 --- a/src/qumada/measurement/measurement.py +++ b/src/qumada/measurement/measurement.py @@ -44,6 +44,7 @@ from qumada.metadata import Metadata from qumada.utils.ramp_parameter import ramp_or_set_parameter from qumada.utils.utils import flatten_array +from qumada.utils.liveplot import MeasurementAndPlot logger = logging.getLogger(__name__) @@ -103,6 +104,7 @@ class MeasurementScript(ABC): """ PARAMETER_NAMES: set[str] = load_param_whitelist() + DEFAULT_LIVE_PLOTTER: callable = None def __init__(self): # Create function hooks for metadata @@ -111,10 +113,15 @@ def __init__(self): self.run = create_hook(self.run, self._add_data_to_metadata) self.run = create_hook(self.run, self._add_current_datetime_to_metadata) + self.live_plotter = self.DEFAULT_LIVE_PLOTTER + self.properties: dict[Any, Any] = {} self.gate_parameters: dict[Any, dict[Any, Parameter | None] | Parameter | None] = {} self._buffered_num_points: int | None = None + def _new_measurement(self, name) -> MeasurementAndPlot: + return MeasurementAndPlot(name=name, gui=self.live_plotter) + def add_gate_parameter(self, parameter_name: str, gate_name: str = None, parameter: Parameter = None) -> None: """ Adds a gate parameter to self.gate_parameters. diff --git a/src/qumada/measurement/scripts/generic_measurement.py b/src/qumada/measurement/scripts/generic_measurement.py index ecd665c3..25498dde 100644 --- a/src/qumada/measurement/scripts/generic_measurement.py +++ b/src/qumada/measurement/scripts/generic_measurement.py @@ -26,7 +26,6 @@ import numpy as np from qcodes.dataset import dond -from qcodes.dataset.measurements import Measurement from qcodes.parameters.specialized_parameters import ElapsedTimeParameter from qumada.instrument.buffers import is_bufferable @@ -248,7 +247,7 @@ def run(self): timestep = self.settings.get("timestep", 1) timer = ElapsedTimeParameter("time") naming_helper(self, default_name="Timetrace") - meas = Measurement(name=self.measurement_name) + meas = self._new_measurement(name=self.measurement_name) meas.register_parameter(timer) for parameter in [*self.gettable_channels, *self.dynamic_channels]: meas.register_parameter( @@ -331,7 +330,7 @@ def run(self): self.generate_lists() naming_helper(self, default_name="Timetrace") - meas = Measurement(name=self.measurement_name) + meas = self._new_measurement(name=self.measurement_name) meas.register_parameter(timer) for parameter in [*self.gettable_channels, *self.dynamic_channels]: @@ -443,7 +442,7 @@ def run(self): timestep = self.settings.get("timestep", 1) # backsweeps = self.settings.get("backsweeps", False) timer = ElapsedTimeParameter("time") - meas = Measurement(name=self.metadata.measurement.name or "timetrace") + meas = self._new_measurement(name=self.metadata.measurement.name or "timetrace") meas.register_parameter(timer) setpoints = [timer] for parameter in self.dynamic_channels: @@ -526,7 +525,7 @@ def run(self): datasets = [] self.generate_lists() naming_helper(self, default_name="Timetrace with sweeps") - meas = Measurement(name=self.measurement_name) + meas = self._new_measurement(name=self.measurement_name) meas.register_parameter(timer) for dynamic_param in self.dynamic_parameters: @@ -685,7 +684,7 @@ def run(self): dynamic_param = self.dynamic_sweeps[i].param inactive_channels = [chan for chan in self.dynamic_channels if chan != dynamic_param] self.initialize(inactive_dyn_channels=inactive_channels) - meas = Measurement(name=self.measurement_name) + meas = self._new_measurement(name=self.measurement_name) meas.register_parameter(dynamic_param) for c_param in self.active_compensating_channels: meas.register_parameter( @@ -890,7 +889,7 @@ def run(self): self.measurement_name += f" {dynamic_parameter['gate']}" self.properties[dynamic_parameter["gate"]][dynamic_parameter["parameter"]]["_is_triggered"] = True dynamic_param = dynamic_sweep.param - meas = Measurement(name=self.measurement_name) + meas = self._new_measurement(name=self.measurement_name) meas.register_parameter(dynamic_param) # This next block is required to log static and idle dynamic # parameters that cannot be buffered. @@ -1083,7 +1082,7 @@ def run(self): gate_names = [gate["gate"] for gate in self.dynamic_parameters] self.measurement_name += f" {gate_names}" - meas = Measurement(name=self.measurement_name) + meas = self._new_measurement(name=self.measurement_name) if reverse_param_order: slow_param = self.dynamic_parameters[1] @@ -1339,7 +1338,7 @@ def run(self): gate_names = [gate["gate"] for gate in self.dynamic_parameters] self.measurement_name += f" {gate_names}" - meas = Measurement(name=self.measurement_name) + meas = self._new_measurement(name=self.measurement_name) meas.register_parameter(timer) for parameter in self.dynamic_parameters: self.properties[parameter["gate"]][parameter["parameter"]]["_is_triggered"] = True @@ -1542,7 +1541,7 @@ def run(self): gate_names = [gate["gate"] for gate in self.dynamic_parameters] self.measurement_name += f" {gate_names}" - meas = Measurement(name=self.measurement_name) + meas = self._new_measurement(name=self.measurement_name) meas.register_parameter(timer) for parameter in self.dynamic_parameters: self.properties[parameter["gate"]][parameter["parameter"]]["_is_triggered"] = True diff --git a/src/qumada/utils/liveplot.py b/src/qumada/utils/liveplot.py index a4361155..bf2dade3 100644 --- a/src/qumada/utils/liveplot.py +++ b/src/qumada/utils/liveplot.py @@ -1,13 +1,18 @@ import contextlib +import functools from collections.abc import Sequence +from typing import Protocol from qcodes import Measurement from qcodes.parameters import ParameterBase +from qcodes.dataset.data_set import DataSet class MeasurementAndPlot: - def __init__(self, *, name: str): + def __init__(self, *, name: str, gui = None): self.qcodes_measurement = Measurement(name=name) + self.gui = gui + def register_parameter( self, parameter: ParameterBase, setpoints: Sequence[str | ParameterBase] | None = None, **kwargs @@ -16,14 +21,27 @@ def register_parameter( @contextlib.contextmanager def run(self): + if self.gui is not None: + # here we could add some more arguments in the future + plot_target = self.gui + else: + plot_target = None + with self.qcodes_measurement.run() as qcodes_datasaver: - yield DataSaverAndPlotter(self, qcodes_datasaver) + yield DataSaverAndPlotter(self, qcodes_datasaver, plot_target) class DataSaverAndPlotter: - def __init__(self, parent: MeasurementAndPlot, qcodes_datasaver): + def __init__(self, parent: MeasurementAndPlot, qcodes_datasaver, plot_target: callable): self._parent = parent self.qcodes_datasaver = qcodes_datasaver + self.plot_target = plot_target def add_result(self, *args): self.qcodes_datasaver.add_result(*args) + if self.plot_target is not None: + self.plot_target(self.dataset.to_xarray_dataset()) + + @property + def dataset(self) -> DataSet: + return self.qcodes_datasaver.dataset From 5efc5f49154440a1d9b092578a74784103df6105 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 25 Jun 2025 15:27:25 +0000 Subject: [PATCH 12/20] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qumada/measurement/measurement.py | 2 +- src/qumada/utils/liveplot.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/qumada/measurement/measurement.py b/src/qumada/measurement/measurement.py index 2849624d..0bd6b143 100644 --- a/src/qumada/measurement/measurement.py +++ b/src/qumada/measurement/measurement.py @@ -42,9 +42,9 @@ from qumada.instrument.buffers import is_bufferable, is_triggerable from qumada.metadata import Metadata +from qumada.utils.liveplot import MeasurementAndPlot from qumada.utils.ramp_parameter import ramp_or_set_parameter from qumada.utils.utils import flatten_array -from qumada.utils.liveplot import MeasurementAndPlot logger = logging.getLogger(__name__) diff --git a/src/qumada/utils/liveplot.py b/src/qumada/utils/liveplot.py index bf2dade3..5e28023b 100644 --- a/src/qumada/utils/liveplot.py +++ b/src/qumada/utils/liveplot.py @@ -4,16 +4,15 @@ from typing import Protocol from qcodes import Measurement -from qcodes.parameters import ParameterBase from qcodes.dataset.data_set import DataSet +from qcodes.parameters import ParameterBase class MeasurementAndPlot: - def __init__(self, *, name: str, gui = None): + def __init__(self, *, name: str, gui=None): self.qcodes_measurement = Measurement(name=name) self.gui = gui - def register_parameter( self, parameter: ParameterBase, setpoints: Sequence[str | ParameterBase] | None = None, **kwargs ): From b1b6de11fcb7376b7e352f66f30699630b52de5a Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Thu, 26 Jun 2025 11:47:11 +0200 Subject: [PATCH 13/20] More tests --- src/tests/device_test.py | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/tests/device_test.py b/src/tests/device_test.py index ab9fee74..0f579353 100644 --- a/src/tests/device_test.py +++ b/src/tests/device_test.py @@ -1,4 +1,5 @@ import dataclasses +import itertools import numpy as np import pytest @@ -85,15 +86,40 @@ def device_test_setup(measurement_test_setup): ) -def test_measured_ramp(device_test_setup): + + + +@pytest.mark.parametrize("buffered,backsweep", itertools.product( + # buffered + [True, False], + # backsweep + [False, True], +)) +def test_measured_ramp(device_test_setup, buffered, backsweep): gate1 = device_test_setup.namespace["gate1"] - (qcodes_data,) = gate1.voltage.measured_ramp(0.4, start=-0.3, buffered=True) - assert gate1.voltage() == pytest.approx(0.4, abs=0.001) + if not buffered: + # TODO: Why is this necessary? Should be handled automatically + device_test_setup.measurement_test_setup.dmm.buffer_n_points.set(1) + device_test_setup.measurement_test_setup.dmm.buffer_SR.set(1) + (qcodes_data,) = gate1.voltage.measured_ramp(0.4, start=-0.3, buffered=buffered, backsweep=backsweep) + if backsweep: + assert gate1.voltage() == pytest.approx(-0.3, abs=0.001) + else: + assert gate1.voltage() == pytest.approx(0.4, abs=0.001) + + if not buffered: + # TODO: Why is this necessary??? + (qcodes_data, _, _) = qcodes_data xarr = qcodes_data.to_xarray_dataset() set_points = xarr.dac_ch01_voltage.values - expected = np.linspace(-0.3, 0.4, len(set_points)) + if backsweep: + fwd = np.linspace(-0.3, 0.4, len(set_points) // 2) + expected = np.concatenate((fwd, fwd[::-1])) + else: + expected = np.linspace(-0.3, 0.4, len(set_points)) + np.testing.assert_almost_equal(expected, set_points) From a3c4a599cc9f2c3e190892c1e4e43ead25a5c76e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 26 Jun 2025 09:47:24 +0000 Subject: [PATCH 14/20] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/tests/device_test.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tests/device_test.py b/src/tests/device_test.py index 0f579353..0d622b21 100644 --- a/src/tests/device_test.py +++ b/src/tests/device_test.py @@ -86,15 +86,15 @@ def device_test_setup(measurement_test_setup): ) - - - -@pytest.mark.parametrize("buffered,backsweep", itertools.product( - # buffered - [True, False], - # backsweep - [False, True], -)) +@pytest.mark.parametrize( + "buffered,backsweep", + itertools.product( + # buffered + [True, False], + # backsweep + [False, True], + ), +) def test_measured_ramp(device_test_setup, buffered, backsweep): gate1 = device_test_setup.namespace["gate1"] From 3709b648eefc1b657df3105225c79616eaaaf63c Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Thu, 26 Jun 2025 12:33:04 +0200 Subject: [PATCH 15/20] Python 3.9 compatible type unions --- src/qumada/utils/liveplot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qumada/utils/liveplot.py b/src/qumada/utils/liveplot.py index 5e28023b..b7c7cdad 100644 --- a/src/qumada/utils/liveplot.py +++ b/src/qumada/utils/liveplot.py @@ -1,7 +1,7 @@ import contextlib import functools from collections.abc import Sequence -from typing import Protocol +from typing import Protocol, Optional, Union from qcodes import Measurement from qcodes.dataset.data_set import DataSet @@ -14,7 +14,7 @@ def __init__(self, *, name: str, gui=None): self.gui = gui def register_parameter( - self, parameter: ParameterBase, setpoints: Sequence[str | ParameterBase] | None = None, **kwargs + self, parameter: ParameterBase, setpoints: Optional[Sequence[Union[str, ParameterBase]]] = None, **kwargs ): self.qcodes_measurement.register_parameter(parameter, setpoints, **kwargs) From 7570d621b6ebcab12a7f6dc1d1cfedc6c0bd7fac Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 26 Jun 2025 10:33:16 +0000 Subject: [PATCH 16/20] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qumada/utils/liveplot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qumada/utils/liveplot.py b/src/qumada/utils/liveplot.py index b7c7cdad..a3072779 100644 --- a/src/qumada/utils/liveplot.py +++ b/src/qumada/utils/liveplot.py @@ -1,7 +1,7 @@ import contextlib import functools from collections.abc import Sequence -from typing import Protocol, Optional, Union +from typing import Optional, Protocol, Union from qcodes import Measurement from qcodes.dataset.data_set import DataSet From 769444f9ae9c19158671e959f19bd1578aa4ac98 Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Thu, 26 Jun 2025 15:28:13 +0200 Subject: [PATCH 17/20] Do not use dummy dmm voltage --- src/tests/device_test.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/tests/device_test.py b/src/tests/device_test.py index 0d622b21..fa366d78 100644 --- a/src/tests/device_test.py +++ b/src/tests/device_test.py @@ -23,7 +23,6 @@ def device_test_setup(measurement_test_setup): parameters = { "ohmic": { - "voltage": {"type": "gettable"}, "current": {"type": "gettable"}, }, "gate1": {"voltage": {"type": "static"}}, @@ -40,7 +39,6 @@ def device_test_setup(measurement_test_setup): mapping = { "ohmic": { - "voltage": measurement_test_setup.dmm.voltage, "current": measurement_test_setup.dmm.current, }, "gate1": { @@ -98,11 +96,6 @@ def device_test_setup(measurement_test_setup): def test_measured_ramp(device_test_setup, buffered, backsweep): gate1 = device_test_setup.namespace["gate1"] - if not buffered: - # TODO: Why is this necessary? Should be handled automatically - device_test_setup.measurement_test_setup.dmm.buffer_n_points.set(1) - device_test_setup.measurement_test_setup.dmm.buffer_SR.set(1) - (qcodes_data,) = gate1.voltage.measured_ramp(0.4, start=-0.3, buffered=buffered, backsweep=backsweep) if backsweep: assert gate1.voltage() == pytest.approx(-0.3, abs=0.001) From 6d65f0a78e1055be5d1721c6c3d95cee3f9e6fb8 Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Thu, 26 Jun 2025 16:18:44 +0200 Subject: [PATCH 18/20] Use dond wrapper for live_plotting --- src/qumada/measurement/measurement.py | 14 ++++++++++++++ .../measurement/scripts/generic_measurement.py | 4 ++-- src/qumada/utils/liveplot.py | 11 +++++++---- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/qumada/measurement/measurement.py b/src/qumada/measurement/measurement.py index 0bd6b143..5f002685 100644 --- a/src/qumada/measurement/measurement.py +++ b/src/qumada/measurement/measurement.py @@ -22,10 +22,12 @@ from __future__ import annotations import copy +import functools import inspect import json import logging import os +import importlib from abc import ABC, abstractmethod from collections.abc import MutableSequence from contextlib import suppress @@ -122,6 +124,18 @@ def __init__(self): def _new_measurement(self, name) -> MeasurementAndPlot: return MeasurementAndPlot(name=name, gui=self.live_plotter) + def _dond(self, *args, **kwargs): + """This is a wrapper around qcodes dond function that monkeypatches the live plotter in the datasaver""" + # we need to use importlib here because the dond function shadows the qcodes.dataset.dond package + do_nd = importlib.import_module("qcodes.dataset.dond.do_nd") + + prev_meas_cls = do_nd.Measurement + try: + do_nd.Measurement = functools.partial(MeasurementAndPlot, gui=self.live_plotter) + return do_nd.dond(*args, **kwargs) + finally: + do_nd.Measurement = prev_meas_cls + def add_gate_parameter(self, parameter_name: str, gate_name: str = None, parameter: Parameter = None) -> None: """ Adds a gate parameter to self.gate_parameters. diff --git a/src/qumada/measurement/scripts/generic_measurement.py b/src/qumada/measurement/scripts/generic_measurement.py index 25498dde..0682315f 100644 --- a/src/qumada/measurement/scripts/generic_measurement.py +++ b/src/qumada/measurement/scripts/generic_measurement.py @@ -95,7 +95,7 @@ def run(self, **dond_kwargs) -> list: self.initialize(inactive_dyn_channels=inactive_channels) sleep(wait_time) data.append( - dond( + self._dond( sweep, *measured_channels, measurement_name=self._measurement_name, @@ -153,7 +153,7 @@ def run(self, **dond_kwargs): for sweep in self.dynamic_sweeps: ramp_or_set_parameter(sweep._param, sweep.get_setpoints()[0]) sleep(wait_time) - data = dond( + data = self._dond( *tuple(self.dynamic_sweeps), *tuple(self.gettable_channels), measurement_name=measurement_name, diff --git a/src/qumada/utils/liveplot.py b/src/qumada/utils/liveplot.py index a3072779..8a27312b 100644 --- a/src/qumada/utils/liveplot.py +++ b/src/qumada/utils/liveplot.py @@ -9,8 +9,8 @@ class MeasurementAndPlot: - def __init__(self, *, name: str, gui=None): - self.qcodes_measurement = Measurement(name=name) + def __init__(self, *, name: str, gui=None, **kwargs): + self.qcodes_measurement = Measurement(name=name, **kwargs) self.gui = gui def register_parameter( @@ -18,15 +18,18 @@ def register_parameter( ): self.qcodes_measurement.register_parameter(parameter, setpoints, **kwargs) + def set_shapes(self, shapes): + self.qcodes_measurement.set_shapes(shapes=shapes) + @contextlib.contextmanager - def run(self): + def run(self, **kwargs): if self.gui is not None: # here we could add some more arguments in the future plot_target = self.gui else: plot_target = None - with self.qcodes_measurement.run() as qcodes_datasaver: + with self.qcodes_measurement.run(**kwargs) as qcodes_datasaver: yield DataSaverAndPlotter(self, qcodes_datasaver, plot_target) From d5492141f5029b67f93843aed5171f891d2e1aaf Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Thu, 26 Jun 2025 16:19:08 +0200 Subject: [PATCH 19/20] Include plotter use in test --- src/tests/device_test.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/tests/device_test.py b/src/tests/device_test.py index fa366d78..6933b945 100644 --- a/src/tests/device_test.py +++ b/src/tests/device_test.py @@ -96,6 +96,13 @@ def device_test_setup(measurement_test_setup): def test_measured_ramp(device_test_setup, buffered, backsweep): gate1 = device_test_setup.namespace["gate1"] + plot_args = [] + def plot_backend(*args, **kwargs): + plot_args.append((args, kwargs)) + + from qumada.measurement.measurement import MeasurementScript + MeasurementScript.DEFAULT_LIVE_PLOTTER = plot_backend + (qcodes_data,) = gate1.voltage.measured_ramp(0.4, start=-0.3, buffered=buffered, backsweep=backsweep) if backsweep: assert gate1.voltage() == pytest.approx(-0.3, abs=0.001) @@ -115,4 +122,9 @@ def test_measured_ramp(device_test_setup, buffered, backsweep): else: expected = np.linspace(-0.3, 0.4, len(set_points)) + if buffered: + assert len(plot_args) == 1 + int(backsweep) + else: + assert len(plot_args) == len(set_points) + np.testing.assert_almost_equal(expected, set_points) From 7b853dffa8c549b9f6aa785335ce0d735975c887 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 26 Jun 2025 14:19:21 +0000 Subject: [PATCH 20/20] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qumada/measurement/measurement.py | 2 +- src/tests/device_test.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qumada/measurement/measurement.py b/src/qumada/measurement/measurement.py index 5f002685..496ccae6 100644 --- a/src/qumada/measurement/measurement.py +++ b/src/qumada/measurement/measurement.py @@ -23,11 +23,11 @@ import copy import functools +import importlib import inspect import json import logging import os -import importlib from abc import ABC, abstractmethod from collections.abc import MutableSequence from contextlib import suppress diff --git a/src/tests/device_test.py b/src/tests/device_test.py index 6933b945..67022fc1 100644 --- a/src/tests/device_test.py +++ b/src/tests/device_test.py @@ -97,10 +97,12 @@ def test_measured_ramp(device_test_setup, buffered, backsweep): gate1 = device_test_setup.namespace["gate1"] plot_args = [] + def plot_backend(*args, **kwargs): plot_args.append((args, kwargs)) from qumada.measurement.measurement import MeasurementScript + MeasurementScript.DEFAULT_LIVE_PLOTTER = plot_backend (qcodes_data,) = gate1.voltage.measured_ramp(0.4, start=-0.3, buffered=buffered, backsweep=backsweep)