From 02a3c6b2b01a0c7a6b9f8564ef8e72c393612750 Mon Sep 17 00:00:00 2001 From: Mark Johnson Date: Mon, 28 Feb 2022 18:27:49 +1100 Subject: [PATCH 1/8] Move the read point based on the trace_pulse amplitude. --- silq/tools/plot_tools.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/silq/tools/plot_tools.py b/silq/tools/plot_tools.py index f1df14662..9583a998e 100644 --- a/silq/tools/plot_tools.py +++ b/silq/tools/plot_tools.py @@ -16,6 +16,7 @@ from qcodes.data.data_set import DataSet from qcodes.data.data_array import DataArray from qcodes.utils.helpers import PerformanceTimer +from silq.meta_instruments.layout import Connection, CombinedConnection __all__ = ['PlotAction', 'SetGates', 'MeasureSingle', 'MoveGates', 'SwitchPlotIdx', 'InteractivePlot', 'SliderPlot', 'CalibrationPlot', @@ -885,8 +886,28 @@ def update_plot(self, initialize=False): result_config['y'] = self.parameter.setpoints[k][0] result_config['z'] = result if self.point is not None: - self.point.set_xdata(self.x_gate.get_latest()) - self.point.set_ydata(self.y_gate.get_latest()) + new_x = self.x_gate.get_latest() + new_y = self.x_gate.get_latest() + + connection = self.layout.get_connection( + self.parameter.trace_pulse.connection_label) + + if isinstance(connection, CombinedConnection): + connections = connection.connections + scales = {c.label: s for c, s in + zip(connections, connection.scale)} + else: + scales = {connection.label:connection.scale} + + # Add scaled offset for "read point" in diagram. + A = self.parameter.trace_pulse.amplitude + if self.x_gate.name in scales: + new_x += A * scales[self.x_gate.name] + if self.y_gate.name in scales: + new_y += A * scales[self.y_gate.name] + + self.point.set_xdata(new_x) + self.point.set_ydata(new_y) else: result_config['y'] = result From ce650a929b61a7312e8c8f27d4497a8191a72f78 Mon Sep 17 00:00:00 2001 From: Mark Johnson Date: Mon, 28 Feb 2022 20:36:44 +1100 Subject: [PATCH 2/8] Refactor, remove single Connection scaling. --- silq/tools/plot_tools.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/silq/tools/plot_tools.py b/silq/tools/plot_tools.py index 9583a998e..794f2e500 100644 --- a/silq/tools/plot_tools.py +++ b/silq/tools/plot_tools.py @@ -888,23 +888,20 @@ def update_plot(self, initialize=False): if self.point is not None: new_x = self.x_gate.get_latest() new_y = self.x_gate.get_latest() - + A = self.parameter.trace_pulse.amplitude connection = self.layout.get_connection( self.parameter.trace_pulse.connection_label) - if isinstance(connection, CombinedConnection): - connections = connection.connections - scales = {c.label: s for c, s in - zip(connections, connection.scale)} - else: - scales = {connection.label:connection.scale} - # Add scaled offset for "read point" in diagram. - A = self.parameter.trace_pulse.amplitude - if self.x_gate.name in scales: - new_x += A * scales[self.x_gate.name] - if self.y_gate.name in scales: - new_y += A * scales[self.y_gate.name] + # Since pulse is already scaled to device voltages, we + # only need to apply the combination scaling. + if isinstance(connection, CombinedConnection): + for con, scale in zip(connection.connections, + connection.scale): + if self.x_gate.name == con.label: + new_x += A * scale + elif self.y_gate.name == con.label: + new_y += A * scale self.point.set_xdata(new_x) self.point.set_ydata(new_y) From c708ddc99ef151c9360f67b8aebd151601e12879 Mon Sep 17 00:00:00 2001 From: Mark Johnson Date: Tue, 1 Mar 2022 09:37:52 +1100 Subject: [PATCH 3/8] Refactor the point drawing into a local method. --- silq/tools/plot_tools.py | 56 +++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/silq/tools/plot_tools.py b/silq/tools/plot_tools.py index 794f2e500..710ed2978 100644 --- a/silq/tools/plot_tools.py +++ b/silq/tools/plot_tools.py @@ -831,6 +831,37 @@ def __init__(self, self.actions = [MoveGates(self)] + def _update_point(self, ax=None): + # This implicitly assumes the trace_pulse has a connection_label and an + # amplitude. There should be no reason that the trace_pulse will not be + # correctly initialized. + new_x = self.x_gate.get_latest() + new_y = self.x_gate.get_latest() + + + connection = self.layout.get_connection( + self.parameter.trace_pulse.connection_label) + + # Add scaled offset for "read point" in diagram. + # Since pulse is already scaled to device voltages, we + # only need to apply the combination scaling. + if isinstance(connection, CombinedConnection): + A = self.parameter.trace_pulse.amplitude + for con, scale in zip(connection.connections, + connection.scale): + if self.x_gate.name == con.label: + new_x += A * scale + elif self.y_gate.name == con.label: + new_y += A * scale + + if self.point is None: + assert ax is not None, "For the initial point to be drawn, axes must" \ + "be provided." + self.point = ax.plot(new_x, new_y, 'o' + self.point_color, ms=5)[0] + else: + self.point.set_xdata(new_x) + self.point.set_ydata(new_y) + def update_plot(self, initialize=False): """Update plot with new 2D DC scan. @@ -869,9 +900,7 @@ def update_plot(self, initialize=False): self.x_gate = getattr(self.station, self.x_label) self.y_gate = getattr(self.station, self.y_label) - self.point = self[k].plot(self.x_gate.get_latest(), - self.y_gate.get_latest(), - 'o' + self.point_color, ms=5)[0] + self._update_point(ax=self[k]) else: self[k].add(result, x=setpoints[0], xlabel=setpoint_names[0], @@ -885,26 +914,7 @@ def update_plot(self, initialize=False): result_config['x'] = self.parameter.setpoints[k][1] result_config['y'] = self.parameter.setpoints[k][0] result_config['z'] = result - if self.point is not None: - new_x = self.x_gate.get_latest() - new_y = self.x_gate.get_latest() - A = self.parameter.trace_pulse.amplitude - connection = self.layout.get_connection( - self.parameter.trace_pulse.connection_label) - - # Add scaled offset for "read point" in diagram. - # Since pulse is already scaled to device voltages, we - # only need to apply the combination scaling. - if isinstance(connection, CombinedConnection): - for con, scale in zip(connection.connections, - connection.scale): - if self.x_gate.name == con.label: - new_x += A * scale - elif self.y_gate.name == con.label: - new_y += A * scale - - self.point.set_xdata(new_x) - self.point.set_ydata(new_y) + self._update_point() else: result_config['y'] = result From c2ecd724d464af6ec449b19e00ade3250664b8e9 Mon Sep 17 00:00:00 2001 From: Mark Johnson Date: Tue, 1 Mar 2022 09:38:08 +1100 Subject: [PATCH 4/8] Remove magic number ylim for trace plot. --- silq/tools/plot_tools.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/silq/tools/plot_tools.py b/silq/tools/plot_tools.py index 710ed2978..1047269fc 100644 --- a/silq/tools/plot_tools.py +++ b/silq/tools/plot_tools.py @@ -800,6 +800,7 @@ class DCSweepPlot(ScanningPlot): **kwargs: Additional kwargs to `InteractivePlot` and ``MatPlot``. """ gate_mapping = {} + trace_ylim = (-0.1, 1.3) point_color = 'r' # DCSweepParameter type @@ -827,7 +828,7 @@ def __init__(self, super().__init__(parameter, subplots=subplots, **kwargs) if parameter.trace_pulse.enabled: - self[1].set_ylim(-0.1, 1.3) + self[1].set_ylim(*self.trace_ylim) self.actions = [MoveGates(self)] From 26b67a78c90959de2222c8d90ab0cd8ab448a7eb Mon Sep 17 00:00:00 2001 From: Mark Johnson Date: Tue, 1 Mar 2022 10:34:32 +1100 Subject: [PATCH 5/8] Trying to fix the axis autoscaling of the trace plot. --- silq/tools/plot_tools.py | 1 + 1 file changed, 1 insertion(+) diff --git a/silq/tools/plot_tools.py b/silq/tools/plot_tools.py index 1047269fc..1cbb6089d 100644 --- a/silq/tools/plot_tools.py +++ b/silq/tools/plot_tools.py @@ -918,6 +918,7 @@ def update_plot(self, initialize=False): self._update_point() else: result_config['y'] = result + result_config['x'] = self.parameter.setpoints[k][0] super().update_plot() From 4dade8d2f00d63d22a23184fe4c657b72315c366 Mon Sep 17 00:00:00 2001 From: Mark Johnson Date: Tue, 26 Apr 2022 15:02:50 +1000 Subject: [PATCH 6/8] Feature: Allow for multiple trace pulses --- silq/parameters/acquisition_parameters.py | 69 ++++++++++++--------- silq/tools/plot_tools.py | 75 +++++++++++++---------- 2 files changed, 82 insertions(+), 62 deletions(-) diff --git a/silq/parameters/acquisition_parameters.py b/silq/parameters/acquisition_parameters.py index 759a31fd3..216e09639 100644 --- a/silq/parameters/acquisition_parameters.py +++ b/silq/parameters/acquisition_parameters.py @@ -834,8 +834,8 @@ def __init__(self, name='DC_sweep', **kwargs): self.sweep_parameters = OrderedDict() # Pulse to acquire trace at the end, disabled by default - self.trace_pulse = DCPulse(name='trace', duration=100e-3, enabled=False, - acquire=True, average='trace', amplitude=0) + self.trace_pulses = [DCPulse(name='trace', duration=100e-3, enabled=False, + acquire=True, average='trace', amplitude=0)] self.pulse_duration = 1e-3 self.final_delay = 120e-3 @@ -876,31 +876,38 @@ def setpoints(self): setpoints = (convert_setpoints(outer_sweep_voltages, inner_sweep_voltages)), - if self.trace_pulse.enabled: - # Also obtain a time trace at the end - points = round(self.trace_pulse.duration * self.sample_rate) - trace_setpoints = tuple( - np.linspace(0, self.trace_pulse.duration, points)) - setpoints += (convert_setpoints(trace_setpoints),) + for trace_pulse in self.trace_pulses: + if trace_pulse.enabled: + # Also obtain a time trace at the end + points = round(trace_pulse.duration * self.sample_rate) + trace_setpoints = tuple( + np.linspace(0, trace_pulse.duration, points)) + setpoints += (convert_setpoints(trace_setpoints),) return setpoints @property_ignore_setter def names(self): - if self.trace_pulse.enabled: - return ('DC_voltage', 'trace_voltage') - else: - return ('DC_voltage',) + names = ('DC_voltage', ) + for trace_pulse in self.trace_pulses: + if trace_pulse.enabled: + names += ('trace_voltage') + return names @property_ignore_setter def labels(self): - if self.trace_pulse.enabled: - return ('DC voltage', 'Trace voltage') - else: - return ('DC voltage',) + labels = ('DC voltage',) + for trace_pulse in self.trace_pulses: + if trace_pulse.enabled: + labels += ('Trace voltage') + return labels @property_ignore_setter def units(self): - return ('V', 'V') if self.trace_pulse.enabled else ('V',) + units = ('V',) + for trace_pulse in self.trace_pulses: + if trace_pulse.enabled: + units += ('V',) + return units @property_ignore_setter def shapes(self): @@ -915,17 +922,20 @@ def shapes(self): outer_sweep_voltages = next(iter_sweep_parameters).sweep_voltages shapes = (len(outer_sweep_voltages), len(inner_sweep_voltages)), - if self.trace_pulse.enabled: - shapes += (round( - self.trace_pulse.duration * self.sample_rate),), + for trace_pulse in self.trace_pulses: + if trace_pulse.enabled: + shapes += (round( + trace_pulse.duration * self.sample_rate),), return shapes @property_ignore_setter def setpoint_names(self): iter_sweep_parameters = reversed(self.sweep_parameters.keys()) names = tuple(iter_sweep_parameters), - if self.trace_pulse.enabled: - names += (('time',), ) + + for trace_pulse in self.trace_pulses: + if trace_pulse.enabled: + names += (('time',), ) return names @property_ignore_setter @@ -933,15 +943,17 @@ def setpoint_labels(self): iter_sweep_parameters = reversed( [(p if p.isupper() else p.capitalize()) for p in self.sweep_parameters.keys()]) labels = tuple(iter_sweep_parameters), - if self.trace_pulse.enabled: - labels += (('Time',),) + for trace_pulse in self.trace_pulses: + if trace_pulse.enabled: + labels += (('Time',),) return labels @property_ignore_setter def setpoint_units(self): setpoint_units = (('V',) * len(self.sweep_parameters),) - if self.trace_pulse.enabled: - setpoint_units += (('s',), ) + for trace_pulse in self.trace_pulses: + if trace_pulse.enabled: + setpoint_units += (('s',), ) return setpoint_units def add_sweep(self, @@ -1123,8 +1135,9 @@ def analyse(self, results = {'DC_voltage': DC_voltages.reshape(self.shapes[0])} - if self.trace_pulse.enabled: - results['trace_voltage'] = traces['trace'][self.channel_label] + for k, trace_pulse in enumerate(self.trace_pulses): + if trace_pulse.enabled: + results[trace_pulse.name] = traces[trace_pulse.name][self.channel_label] return results diff --git a/silq/tools/plot_tools.py b/silq/tools/plot_tools.py index 1cbb6089d..298f98523 100644 --- a/silq/tools/plot_tools.py +++ b/silq/tools/plot_tools.py @@ -801,7 +801,6 @@ class DCSweepPlot(ScanningPlot): """ gate_mapping = {} trace_ylim = (-0.1, 1.3) - point_color = 'r' # DCSweepParameter type def __init__(self, @@ -812,14 +811,16 @@ def __init__(self, if gate_mapping is not None: self.gate_mapping = gate_mapping - if parameter.trace_pulse.enabled: - subplots = (2, 1) - kwargs['gridspec_kw'] = {'height_ratios': [2, 1]} - kwargs['figsize'] = kwargs.get('figsize', (6.5, 6)) + num_traces = np.count_nonzero([trace_pulse.enabled + for trace_pulse in parameter.trace_pulses]) + if num_traces > 0: + subplots = (num_traces, 1) + kwargs['gridspec_kw'] = {'height_ratios': [2, 1]} + kwargs['figsize'] = kwargs.get('figsize', (6.5, 6)) else: subplots = 1 - self.point = None + self.points = {} self.buffer_length = averages self.buffers = [None, ] * self.buffer_length @@ -827,41 +828,47 @@ def __init__(self, super().__init__(parameter, subplots=subplots, **kwargs) - if parameter.trace_pulse.enabled: - self[1].set_ylim(*self.trace_ylim) + for k, trace_pulse in enumerate(parameter.trace_pulses): + if trace_pulse.enabled: + self[k].set_ylim(*self.trace_ylim) self.actions = [MoveGates(self)] - def _update_point(self, ax=None): + def _update_points(self, ax=None): # This implicitly assumes the trace_pulse has a connection_label and an # amplitude. There should be no reason that the trace_pulse will not be # correctly initialized. new_x = self.x_gate.get_latest() new_y = self.x_gate.get_latest() - - connection = self.layout.get_connection( - self.parameter.trace_pulse.connection_label) - - # Add scaled offset for "read point" in diagram. - # Since pulse is already scaled to device voltages, we - # only need to apply the combination scaling. - if isinstance(connection, CombinedConnection): - A = self.parameter.trace_pulse.amplitude - for con, scale in zip(connection.connections, - connection.scale): - if self.x_gate.name == con.label: - new_x += A * scale - elif self.y_gate.name == con.label: - new_y += A * scale - - if self.point is None: - assert ax is not None, "For the initial point to be drawn, axes must" \ - "be provided." - self.point = ax.plot(new_x, new_y, 'o' + self.point_color, ms=5)[0] - else: - self.point.set_xdata(new_x) - self.point.set_ydata(new_y) + for k, trace_pulse in enumerate(self.parameter.trace_pulses): + if trace_pulse.enabled: + connection = self.layout.get_connection( + trace_pulse.connection_label) + + # Add scaled offset for "read point" in diagram. + # Since pulse is already scaled to device voltages, we + # only need to apply the combination scaling. + if isinstance(connection, CombinedConnection): + A = trace_pulse.amplitude + for con, scale in zip(connection.connections, + connection.scale): + if self.x_gate.name == con.label: + new_x += A * scale + elif self.y_gate.name == con.label: + new_y += A * scale + + if not self.points: + assert ax is not None, "For the initial point to be drawn, axes must" \ + "be provided." + self.points[k] = ax.plot(new_x, new_y, marker='o', + color=f'C{k}', ms=5, + label=trace_pulse.name)[0] + else: + self.points[k].set_xdata(new_x) + self.points[k].set_ydata(new_y) + else: + pass def update_plot(self, initialize=False): """Update plot with new 2D DC scan. @@ -901,7 +908,7 @@ def update_plot(self, initialize=False): self.x_gate = getattr(self.station, self.x_label) self.y_gate = getattr(self.station, self.y_label) - self._update_point(ax=self[k]) + self._update_points(ax=self[k]) else: self[k].add(result, x=setpoints[0], xlabel=setpoint_names[0], @@ -915,7 +922,7 @@ def update_plot(self, initialize=False): result_config['x'] = self.parameter.setpoints[k][1] result_config['y'] = self.parameter.setpoints[k][0] result_config['z'] = result - self._update_point() + self._update_points() else: result_config['y'] = result result_config['x'] = self.parameter.setpoints[k][0] From 22f85fbdc6fd0d18e21a40a898fd7c5af2a0c615 Mon Sep 17 00:00:00 2001 From: Mark Johnson Date: Tue, 26 Apr 2022 16:40:31 +1000 Subject: [PATCH 7/8] Bug fixing. --- silq/parameters/acquisition_parameters.py | 15 +++++++++------ silq/tools/plot_tools.py | 22 +++++++++++++--------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/silq/parameters/acquisition_parameters.py b/silq/parameters/acquisition_parameters.py index 216e09639..08442cf8a 100644 --- a/silq/parameters/acquisition_parameters.py +++ b/silq/parameters/acquisition_parameters.py @@ -890,7 +890,7 @@ def names(self): names = ('DC_voltage', ) for trace_pulse in self.trace_pulses: if trace_pulse.enabled: - names += ('trace_voltage') + names += (trace_pulse.name,) return names @property_ignore_setter @@ -898,7 +898,7 @@ def labels(self): labels = ('DC voltage',) for trace_pulse in self.trace_pulses: if trace_pulse.enabled: - labels += ('Trace voltage') + labels += (trace_pulse.name,) return labels @property_ignore_setter @@ -1093,9 +1093,12 @@ def generate(self): raise NotImplementedError( f"Cannot handle {len(self.sweep_parameters)} parameters") - if self.trace_pulse.enabled: - # Also obtain a time trace at the end - pulses.append(self.trace_pulse) + for trace_pulse in self.trace_pulses: + if trace_pulse.enabled: + # Explicitly add trace_pulses after each other at the end of + # the pulse sequence. + trace_pulse.t_start = pulses[-1].t_stop + pulses.append(trace_pulse) self.pulse_sequence = PulseSequence(pulses=pulses) self.pulse_sequence.final_delay = self.final_delay @@ -1135,7 +1138,7 @@ def analyse(self, results = {'DC_voltage': DC_voltages.reshape(self.shapes[0])} - for k, trace_pulse in enumerate(self.trace_pulses): + for k, trace_pulse in enumerate(self.trace_pulses, 1): if trace_pulse.enabled: results[trace_pulse.name] = traces[trace_pulse.name][self.channel_label] diff --git a/silq/tools/plot_tools.py b/silq/tools/plot_tools.py index 298f98523..0f6ef65ab 100644 --- a/silq/tools/plot_tools.py +++ b/silq/tools/plot_tools.py @@ -814,8 +814,8 @@ def __init__(self, num_traces = np.count_nonzero([trace_pulse.enabled for trace_pulse in parameter.trace_pulses]) if num_traces > 0: - subplots = (num_traces, 1) - kwargs['gridspec_kw'] = {'height_ratios': [2, 1]} + subplots = (1 + num_traces, 1) + kwargs['gridspec_kw'] = {'height_ratios': [2] + [1]*num_traces} kwargs['figsize'] = kwargs.get('figsize', (6.5, 6)) else: subplots = 1 @@ -828,7 +828,7 @@ def __init__(self, super().__init__(parameter, subplots=subplots, **kwargs) - for k, trace_pulse in enumerate(parameter.trace_pulses): + for k, trace_pulse in enumerate(parameter.trace_pulses, 1): if trace_pulse.enabled: self[k].set_ylim(*self.trace_ylim) @@ -838,8 +838,8 @@ def _update_points(self, ax=None): # This implicitly assumes the trace_pulse has a connection_label and an # amplitude. There should be no reason that the trace_pulse will not be # correctly initialized. - new_x = self.x_gate.get_latest() - new_y = self.x_gate.get_latest() + x_ref = self.x_gate.get_latest() + y_ref = self.y_gate.get_latest() for k, trace_pulse in enumerate(self.parameter.trace_pulses): if trace_pulse.enabled: @@ -849,6 +849,9 @@ def _update_points(self, ax=None): # Add scaled offset for "read point" in diagram. # Since pulse is already scaled to device voltages, we # only need to apply the combination scaling. + new_x = x_ref + new_y = y_ref + if isinstance(connection, CombinedConnection): A = trace_pulse.amplitude for con, scale in zip(connection.connections, @@ -858,15 +861,16 @@ def _update_points(self, ax=None): elif self.y_gate.name == con.label: new_y += A * scale - if not self.points: + if trace_pulse.name not in self.points: assert ax is not None, "For the initial point to be drawn, axes must" \ "be provided." - self.points[k] = ax.plot(new_x, new_y, marker='o', + self.points[trace_pulse.name] = ax.plot(new_x, new_y, + marker='o', linestyle='', color=f'C{k}', ms=5, label=trace_pulse.name)[0] else: - self.points[k].set_xdata(new_x) - self.points[k].set_ydata(new_y) + self.points[trace_pulse.name].set_xdata(new_x) + self.points[trace_pulse.name].set_ydata(new_y) else: pass From ce9be8601cb2846c7393d2d3ddc21c76406c95dd Mon Sep 17 00:00:00 2001 From: Mark Johnson Date: Tue, 26 Apr 2022 17:01:27 +1000 Subject: [PATCH 8/8] Adjust subplots to fit a bit tighter in the window. --- silq/tools/plot_tools.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/silq/tools/plot_tools.py b/silq/tools/plot_tools.py index 0f6ef65ab..60564dac5 100644 --- a/silq/tools/plot_tools.py +++ b/silq/tools/plot_tools.py @@ -10,7 +10,7 @@ import logging import qcodes as qc -from qcodes.plots.qcmatplotlib import MatPlot +from qcodes.plots.qcmatplotlib import MatPlot, align_x_axis from qcodes.instrument.parameter import _BaseParameter from qcodes.station import Station from qcodes.data.data_set import DataSet @@ -815,8 +815,8 @@ def __init__(self, for trace_pulse in parameter.trace_pulses]) if num_traces > 0: subplots = (1 + num_traces, 1) - kwargs['gridspec_kw'] = {'height_ratios': [2] + [1]*num_traces} - kwargs['figsize'] = kwargs.get('figsize', (6.5, 6)) + kwargs['gridspec_kw'] = {'height_ratios': [1.5] + [0.5]*num_traces} + kwargs['figsize'] = kwargs.get('figsize', (5, 4 + 1*num_traces)) else: subplots = 1 @@ -827,10 +827,11 @@ def __init__(self, self.buf_idx = 0 super().__init__(parameter, subplots=subplots, **kwargs) - + self.tight_layout() for k, trace_pulse in enumerate(parameter.trace_pulses, 1): if trace_pulse.enabled: self[k].set_ylim(*self.trace_ylim) + align_x_axis(self[k], self[0]) self.actions = [MoveGates(self)]