def test_steppeing_from_invalid_starting_point(self): the_value = -10 def set_function(value): nonlocal the_value the_value = value def get_function(): return the_value a = Parameter('test', set_cmd=set_function, get_cmd=get_function, vals=Numbers(0, 100), step=5) # We start out by setting the parameter to an # invalid value. This is not possible using initial_value # as the validator will catch that but perhaps this may happen # if the instrument can return out of range values. assert a.get() == -10 with pytest.raises(ValueError): # trying to set to 10 should raise even with 10 valid # as the steps demand that we first step to -5 which is not a.set(10) # afterwards the value should still be the same assert a.get() == -10
def test_get_on_parameter_marked_as_non_gettable_raises(): a = Parameter("param") a._gettable = False with pytest.raises( TypeError, match="Trying to get a parameter that is not gettable."): a.get()
def test_set_on_parameter_marked_as_non_settable_raises(): a = Parameter("param", set_cmd=None) a.set(2) assert a.get() == 2 a._settable = False with pytest.raises( TypeError, match="Trying to set a parameter that is not settable."): a.set(1) assert a.get() == 2
def test_scale_and_offset_raw_value_iterable_for_set_cache(values, offsets, scales): p = Parameter(name='test_scale_and_offset_raw_value', set_cmd=None) # test that scale and offset does not change the default behaviour p.cache.set(values) assert p.raw_value == values # test setting scale and offset does not change anything p.scale = scales p.offset = offsets assert p.raw_value == values np_values = np.array(values) np_offsets = np.array(offsets) np_scales = np.array(scales) np_get_latest_values = np.array(p.get_latest()) # Without a call to ``get``, ``get_latest`` will just return old # cached values without applying the set scale and offset np.testing.assert_allclose(np_get_latest_values, np_values) np_get_values = np.array(p.get()) # Now that ``get`` is called, the returned values are the result of # application of the scale and offset. Obviously, calling # ``get_latest`` now will also return the values with the applied # scale and offset np.testing.assert_allclose(np_get_values, (np_values - np_offsets) / np_scales) np_get_latest_values_after_get = np.array(p.get_latest()) np.testing.assert_allclose(np_get_latest_values_after_get, (np_values - np_offsets) / np_scales) # test ``cache.set`` for scalar values if not isinstance(values, Iterable): p.cache.set(values) np.testing.assert_allclose(np.array(p.raw_value), np_values * np_scales + np_offsets) # No set/get cmd performed # testing conversion back and forth p.cache.set(values) np_get_latest_values = np.array(p.get_latest()) # No set/get cmd performed np.testing.assert_allclose(np_get_latest_values, np_values) # adding statistics if isinstance(offsets, Iterable): event('Offset is array') if isinstance(scales, Iterable): event('Scale is array') if isinstance(values, Iterable): event('Value is array') if isinstance(scales, Iterable) and isinstance(offsets, Iterable): event('Scale is array and also offset') if isinstance(scales, Iterable) and not isinstance(offsets, Iterable): event('Scale is array but not offset')
def test_gettable(self): p = Parameter('p', get_cmd=self.get_p) self._p = 21 self.assertEqual(p(), 21) self.assertEqual(p.get(), 21) with self.assertRaises(NotImplementedError): p(10) self.assertTrue(hasattr(p, 'get')) self.assertFalse(hasattr(p, 'set'))
def test_bare_function(self): # not a use case we want to promote, but it's there... p = Parameter('test', get_cmd=None, set_cmd=None) def doubler(x): p.set(x * 2) f = Function('f', call_cmd=doubler, args=[vals.Numbers(-10, 10)]) f(4) self.assertEqual(p.get(), 8) with self.assertRaises(ValueError): f(20)
def test_get_cache_no_get(): """ Test that cache.get on a parameter that does not have get is handled correctly. """ local_parameter = Parameter('test_param', set_cmd=None, get_cmd=False) # The parameter does not have a get method. with pytest.raises(AttributeError): local_parameter.get() # get_latest will fail as get cannot be called and no cache # is available with pytest.raises(RuntimeError): local_parameter.cache.get() value = 1 local_parameter.set(value) assert local_parameter.cache.get() == value local_parameter2 = Parameter('test_param2', set_cmd=None, get_cmd=False, initial_value=value) with pytest.raises(AttributeError): local_parameter2.get() assert local_parameter2.cache.get() == value
def test_set_via_function(): # not a use case we want to promote, but it's there... p = Parameter('test', get_cmd=None, set_cmd=None) def doubler(x): p.set(x * 2) f = Function('f', call_cmd=doubler, args=[vals.Numbers(-10, 10)]) f(4) assert p.get() == 8 with pytest.raises(ValueError): f(20)
def create_parameter(snapshot_get: bool, snapshot_value: bool, cache_is_valid: bool, get_cmd: Optional[Union[Callable[..., Any], bool]], offset: Union[str, float] = NOT_PASSED): kwargs: Dict[str, Any] = dict(set_cmd=None, label='Parameter', unit='a.u.', docstring='some docs') if offset != NOT_PASSED: kwargs.update(offset=offset) if snapshot_get != NOT_PASSED: kwargs.update(snapshot_get=snapshot_get) if snapshot_value != NOT_PASSED: kwargs.update(snapshot_value=snapshot_value) if get_cmd != NOT_PASSED: kwargs.update(get_cmd=get_cmd) p = Parameter('p', **kwargs) if get_cmd is not False: def wrap_in_call_counter(get_func): call_count = 0 def wrapped_func(*args, **kwargs): nonlocal call_count call_count += 1 return get_func(*args, **kwargs) wrapped_func.call_count = lambda: call_count return wrapped_func p.get = wrap_in_call_counter(p.get) # pre-condition assert p.get.call_count() == 0 # type: ignore[attr-defined] else: # pre-condition assert not hasattr(p, 'get') assert not p.gettable if cache_is_valid: p.set(42) return p
def array_in_str_dataset(experiment, request): meas = Measurement() scalar_param = Parameter('textparam', set_cmd=None) param = ArraySetPointParam() meas.register_parameter(scalar_param, paramtype='text') meas.register_parameter(param, setpoints=(scalar_param,), paramtype=request.param) with meas.run() as datasaver: for i in ['A', 'B', 'C']: scalar_param.set(i) datasaver.add_result((scalar_param, scalar_param.get()), (param, param.get())) try: yield datasaver.dataset finally: datasaver.dataset.conn.close()
def array_in_scalar_dataset_unrolled(experiment): meas = Measurement() scalar_param = Parameter('scalarparam', set_cmd=None) param = ArraySetPointParam() meas.register_parameter(scalar_param) meas.register_parameter(param, setpoints=(scalar_param,), paramtype='numeric') with meas.run() as datasaver: for i in range(1, 10): scalar_param.set(i) datasaver.add_result((scalar_param, scalar_param.get()), (param, param.get())) try: yield datasaver.dataset finally: datasaver.dataset.conn.close()
def test_ramp_scaled(self, scale, value): p = Parameter('p', set_cmd=self.set_p, get_cmd=self.get_p, scale=scale, initial_value=0) assert p() == 0.0 # first set a step size p.step = 0.1 # and a wait time p.inter_delay = 1e-9 # in seconds expected_output = np.linspace(0.1, 10, 100) np.testing.assert_allclose(p.get_ramp_values(10, p.step), expected_output) p.set(value) np.testing.assert_allclose(p.get(), value) assert p.raw_value == value * scale
def varlen_array_in_scalar_dataset(experiment): meas = Measurement() scalar_param = Parameter('scalarparam', set_cmd=None) param = ArraySetPointParam() meas.register_parameter(scalar_param) meas.register_parameter(param, setpoints=(scalar_param,), paramtype='array') np.random.seed(0) with meas.run() as datasaver: for i in range(1, 10): scalar_param.set(i) param.setpoints = (np.arange(i),) datasaver.add_result((scalar_param, scalar_param.get()), (param, np.random.rand(i))) try: yield datasaver.dataset finally: datasaver.dataset.conn.close()
def test_gettable(): mem = ParameterMemory() p = Parameter('p', get_cmd=mem.get) mem.set(21) assert p() == 21 assert p.get() == 21 with pytest.raises(NotImplementedError): p(10) assert hasattr(p, 'get') assert p.gettable assert not hasattr(p, 'set') assert not p.settable p.cache.set(7) assert p.get_latest() == 7 # Nothing has been passed to the "instrument" at ``cache.set`` # call, hence the following assertions should hold assert mem.get() == 21 assert p() == 21 assert p.get_latest() == 21
def array_in_scalar_dataset_unrolled(experiment): """ This fixture yields a dataset where an array-valued parameter is registered as a 'numeric' type and has an additional single-valued setpoint. We expect data to be saved as individual scalars, with the scalar setpoint repeated. """ meas = Measurement() scalar_param = Parameter('scalarparam', set_cmd=None) param = ArraySetPointParam() meas.register_parameter(scalar_param) meas.register_parameter(param, setpoints=(scalar_param, ), paramtype='numeric') with meas.run() as datasaver: for i in range(1, 10): scalar_param.set(i) datasaver.add_result((scalar_param, scalar_param.get()), (param, param.get())) try: yield datasaver.dataset finally: datasaver.dataset.conn.close()
class VideoMode: """ Controls the videomode tuning. Attributes: station (qcodes station): contains all the information about the set-up sweepparams (string, 1 x 2 list or dict): the parameter(s) to be swept sweepranges (int or 1 x 2 list): the range(s) to be swept over (peak-to-peak range) minstrument (int or tuple): the channel of the FPGA, or tuple (instrument, channel) Naverage (int): the number of times the FPGA averages resolution (1 x 2 list): for 2D the resolution nplots (int or None): number of plots to show. must be equal to the number of channels in the minstrument argument sample_rate (float): sample rate for acquisition device crosshair (bool): enable crosshair """ videomode_class_index = 0 def __init__(self, station, sweepparams, sweepranges, minstrument, nplots=None, Naverage=10, resolution=[96, 96], sample_rate='default', diff_dir=None, verbose=1, dorun=True, show_controls=True, add_ppt=True, crosshair=False, averaging=True, name=None, mouse_click_callback=None): """ Tool for fast acquisition of charge stability diagram The class assumes that the station has a station.digitizer and a station.awg. Args: station (QCoDeS station): reference for the instruments sweepparams (list or dict or str): parameters to sweep mouse_click_callback (None or function): if None then update scan position with callback """ if VideoMode.videomode_class_index == 0: # create instance of QApplication _ = pyqtgraph.mkQApp() VideoMode.videomode_class_index = VideoMode.videomode_class_index + 1 # increment index of VideoMode objects self.videomode_index = VideoMode.videomode_class_index self.station = station self.verbose = verbose self.sweepparams = sweepparams self.sweepranges = sweepranges self.virtual_awg = getattr(station, ' virtual_awg', None) self.minstrumenthandle = minstrument[0] self.channels = minstrument[1] if isinstance(self.channels, int): self.channels = [self.channels] self.Naverage = Parameter('Naverage', get_cmd=self._get_Naverage, set_cmd=self._set_Naverage, vals=Numbers(1, 1023)) self._Naverage_val = Naverage self.resolution = resolution self.sample_rate = sample_rate self.diff_dir = diff_dir self.datalock = threading.Lock() self.datafunction_result = None self.update_sleep = 1e-5 if name is None: name = 'VideoMode %d' % self.videomode_index if isinstance(self.sweepparams, (str, list)): name += ': %s' % str(self.sweepparams) self.name = name # parse instrument minstrumenthandle = qtt.measurements.scans.get_instrument(self.minstrumenthandle, station) if minstrumenthandle.name in ['digitizer', 'm4i']: if sample_rate == 'default': self.sampling_frequency = minstrumenthandle.sample_rate else: minstrumenthandle.sample_rate(sample_rate) self.sampling_frequency = station.digitizer.sample_rate elif minstrumenthandle.name in ['ZIUHFLI', 'ziuhfli']: self.sampling_frequency = minstrumenthandle.scope_samplingrate else: try: minstrumenthandle = qtt.measurements.scans.get_instrument( minstrument, station) self.sampling_frequency = minstrumenthandle.sample_rate except: raise Exception('no fpga or digitizer found') # for addPPT and debugging self.scanparams = {'sweepparams': sweepparams, 'sweepranges': sweepranges, 'minstrument': minstrument, 'resolution': self.resolution, 'sampling_frequency': self.sampling_frequency()} self.idx = 0 self.fps = qtt.pgeometry.fps_t(nn=6) # Setup the GUI if nplots is None: nplots = len(self.channels) self.nplots = nplots self.window_title = "%s: nplots %d" % ( self.name, self.nplots) win = QtWidgets.QWidget() win.setWindowTitle(self.window_title) self.mainwin = win vertLayout = QtWidgets.QVBoxLayout() self.topLayout = None if show_controls: topLayout = QtWidgets.QHBoxLayout() win.start_button = QtWidgets.QPushButton('Start') win.stop_button = QtWidgets.QPushButton('Stop') topLayout.addWidget(win.start_button) topLayout.addWidget(win.stop_button) if add_ppt: win.ppt_button = QtWidgets.QPushButton('Copy to PPT') win.ppt_button.clicked.connect(connect_slot(self.addPPT)) topLayout.addWidget(win.ppt_button) win.averaging_box = QtWidgets.QCheckBox('Averaging') for b in [win.start_button, win.stop_button]: b.setMaximumHeight(24) topLayout.addWidget(win.averaging_box) vertLayout.addLayout(topLayout) self.topLayout = topLayout win.setLayout(vertLayout) self.plotLayout = QtWidgets.QHBoxLayout() vertLayout.addLayout(self.plotLayout) self.lp = [] for ii in range(nplots): lp = livePlot(None, self.station.gates, self.sweepparams, self.sweepranges, show_controls=False, plot_title=str(self.minstrumenthandle) + ' ' + str(self.channels[ii])) self.lp.append(lp) self.plotLayout.addWidget(self.lp[ii].win) if show_controls: self.mainwin.averaging_box.clicked.connect( connect_slot(self.enable_averaging_slot)) self.mainwin.start_button.clicked.connect(connect_slot(self.run)) self.mainwin.stop_button.clicked.connect(connect_slot(self.stop)) self.box = self._create_naverage_box(Naverage=Naverage) self.topLayout.addWidget(self.box) self.setGeometry = self.mainwin.setGeometry self.mainwin.resize(800, 600) self.mainwin.show() self.timer = qtpy.QtCore.QTimer() self.timer.timeout.connect(self.updatebg) self.crosshair(show=crosshair) self.enable_averaging_slot(averaging=averaging) if mouse_click_callback is None: mouse_click_callback = self._update_position for p in self.lp: p.sigMouseClicked.connect(mouse_click_callback) if dorun: self.run() def __repr__(self): s = '%s: %s at 0x%x' % (self.__class__.__name__, self.name, id(self)) return s def _update_position(self, position, verbose=1): """ Update position of gates with selected point Args: position (array): point with new coordinates verbose (int): verbosity level """ station = self.station if verbose: print('# %s: update position: %s' % (self.__class__.__name__, position,)) if self.scan_dimension() == 1: delta = position[0] if isinstance(self.scanparams['sweepparams'], str): param = getattr(station.gates, self.scanparams['sweepparams']) param.set(delta) if verbose > 2: print(' set %s to %s' % (param, delta)) return try: for ii, parameter in enumerate(self.scanparams['sweepparams']): delta = position[ii] if verbose: print('param %s: delta %.3f' % (parameter, delta)) if isinstance(parameter, str): if parameter == 'gates_horz' or parameter == 'gates_vert': d = self.scanparams['sweepparams'][parameter] for g, f in d.items(): if verbose >= 2: print(' %s: increment %s with %s' % (parameter, g, f * delta)) param = getattr(station.gates, g) param.increment(f * delta) else: if verbose > 2: print(' set %s to %s' % (parameter, delta)) param = getattr(station.gates, parameter) param.set(delta) else: raise Exception('_update_position with parameter type %s not supported' % (type(parameter),)) except Exception as ex: logging.exception(ex) def enable_averaging_slot(self, averaging=None, *args, **kwargs): """ Update the averaging mode of the widget """ if averaging is None: self._averaging_enabled = self.mainwin.averaging_box.checkState() else: self._averaging_enabled = averaging self.mainwin.averaging_box.setChecked(self._averaging_enabled) for l in self.lp: l.enable_averaging(self._averaging_enabled) def addPPT(self): """ Copy image of videomode window to PPT """ isrunning = self.is_running() if isrunning: self.stopreadout() # prevent multi-threading issues time.sleep(0.2) setting_notes = 'moving averaging enabled: %d\n' % self._averaging_enabled setting_notes += 'number of averages %d\n' % self.Naverage() qtt.utilities.tools.addPPTslide(fig=self, title='VideoMode %s' % self.name, notes=self.station, extranotes='date: %s' % (qtt.data.dateString(),) + '\n' + 'scanjob: ' + str( self.scanparams) + '\n\n' + setting_notes) if isrunning: self.startreadout() def updatebg(self): """ Update function for the widget Calls the datafunction() and update() function for all subplots """ if self.idx % 10 == 0: logging.debug('%s: updatebg %d' % (self.__class__.__name__, self.idx)) self.idx = self.idx + 1 self.fps.addtime(time.time()) if self.datafunction is not None: try: dd = self.datafunction() self.datafunction_result = dd for ii, d in enumerate(dd): self.lp[ii].update(data=d, processevents=False) pyqtgraph.mkQApp().processEvents() except Exception as e: logging.exception(e) print('%s: Exception in updatebg, stopping readout' % self.__class__.__name__) self.stopreadout() else: self.stopreadout() dd = None self.mainwin.setWindowTitle( self.window_title + ' %.1f [fps]' % self.fps.framerate()) if self.fps.framerate() < 10: time.sleep(0.1) time.sleep(self.update_sleep) def is_running(self): return self.timer.isActive() def startreadout(self, callback=None, rate=30, maxidx=None): """ Args: rate (float): sample rate in ms """ if maxidx is not None: self.maxidx = maxidx if callback is not None: self.datafunction = callback self.timer.start(1000 * (1. / rate)) if self.verbose: print('%s: start readout' % (self.__class__.__name__,)) def stopreadout(self): if self.verbose: print('%s: stop readout' % (self.__class__.__name__,)) self.timer.stop() self.mainwin.setWindowTitle(self.window_title + ' stopped') def _create_naverage_box(self, Naverage=1): box = QtWidgets.QSpinBox() box.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) # do not emit signals when still editing box.setKeyboardTracking(False) box.setMinimum(1) box.setMaximum(1023) box.setPrefix('Naverage: ') box.setValue(Naverage) box.setMaximumWidth(120) box.valueChanged.connect(self.Naverage.set) return box def close(self): self.mainwin.close() def get_dataset(self, run=False): """ Return latest recorded dataset Returns: alldata (dataset or list of datasets) """ with self.datalock: if run: warnings.warn('not supported') # [l.datafunction_result for l in self.lp] data = self.datafunction_result data = np.array(data) else: data = self.datafunction_result if data is not None: data = np.array(data) if data is not None: self.alldata = self.makeDataset(data, Naverage=None) return self.alldata def scan_dimension(self): if isinstance(self.sweepranges, (float, int)): return 1 elif isinstance(self.sweepranges, list): if len(self.sweepranges) == 2: return 2 elif len(self.sweepranges) == 1: return 1 else: raise Exception('scan dimension not supported') else: return -1 def run(self, start_readout=True): """ Programs the AWG, starts the read-out and the plotting. """ if self.verbose: print('%s: run ' % (self.__class__.__name__,) ) scan_dimensions = self.scan_dimension() virtual_awg = getattr(self.station, 'virtual_awg', None) awg = getattr(self.station, 'awg', None) if scan_dimensions == 1: self.__run_1d_scan(awg, virtual_awg) elif scan_dimensions == 2: self.__run_2d_scan(awg, virtual_awg) else: raise Exception('type of scan not supported') if start_readout: if self.verbose: print('%s: run: startreadout' % (self.__class__.__name__,)) self.startreadout() def __run_1d_scan(self, awg, virtual_awg, period=1e-3): if virtual_awg: if not isinstance(self.sweepparams, (str, dict)): raise Exception('arguments not supported') sweep_range = self.sweepranges gates = self.sweepparams if isinstance(self.sweepparams, dict) else {self.sweepparams: 1} waveform = virtual_awg.sweep_gates(gates, sweep_range, period) virtual_awg.enable_outputs(list(gates.keys())) virtual_awg.run() else: if type(self.sweepparams) is str: waveform, _ = awg.sweep_gate(self.sweepparams, self.sweepranges, period=period) elif type(self.sweepparams) is dict: waveform, _ = awg.sweep_gate_virt(self.sweepparams, self.sweepranges, period=period) else: raise Exception('arguments not supported') self.datafunction = videomode_callback(self.station, waveform, self.Naverage.get(), minstrument=(self.minstrumenthandle, self.channels)) def __run_2d_scan(self, awg, virtual_awg, period=None): if virtual_awg: sweep_ranges = [i for i in self.sweepranges] if isinstance(self.sweepparams[0], dict): gates = self.sweepparams else: # convert list of strings to dict format gates = self.sweepparams gates = [dict([(gate, 1)]) for gate in gates] if period is None: sampling_frequency = qtt.measurements.scans.get_sampling_frequency(self.minstrumenthandle) base_period = 1. / sampling_frequency total_period = base_period * np.prod(self.resolution) else: total_period = period warnings.warn('code untested') waveform = virtual_awg.sweep_gates_2d(gates, sweep_ranges, total_period, self.resolution) keys = [list(item.keys())[0] for item in gates] virtual_awg.enable_outputs(keys) virtual_awg.run() else: if isinstance(self.sweepparams, list): waveform, _ = awg.sweep_2D(self.sampling_frequency.get(), self.sweepparams, self.sweepranges, self.resolution) elif isinstance(self.sweepparams, dict): waveform, _ = awg.sweep_2D_virt(self.sampling_frequency.get(), self.sweepparams[ 'gates_horz'], self.sweepparams['gates_vert'], self.sweepranges, self.resolution) else: raise Exception('arguments not supported') if self.verbose: print('%s: 2d scan, define callback ' % (self.__class__.__name__,)) self.datafunction = videomode_callback(self.station, waveform, self.Naverage.get(), minstrument=(self.minstrumenthandle, self.channels), resolution=self.resolution, diff_dir=self.diff_dir) def stop(self): """ Stops the plotting, AWG(s) and if available RF. """ self.stopreadout() if self.station is not None: if hasattr(self.station, 'awg'): self.station.awg.stop() if hasattr(self.station, 'RF'): self.station.RF.off() if hasattr(self.station, 'virtual_awg'): self.station.virtual_awg.stop() def single(self): """Do a single scan with a lot averaging. Note: this does not yet support the usage of linear combinations of gates (a.k.a. virtual gates). """ raise Exception('not implemented') def crosshair(self, *args, **kwargs): for l in self.lp: l.crosshair(*args, **kwargs) def makeDataset(self, data, Naverage=None): """ Helper function """ metadata = {'scantime': str(datetime.datetime.now( )), 'station': self.station.snapshot(), 'allgatevalues': self.station.gates.allvalues()} metadata['Naverage'] = Naverage if hasattr(self.datafunction, 'diff_dir'): metadata['diff_dir'] = self.datafunction.diff_dir if data.ndim == 2: if (data.shape[0] > 1): raise Exception('not yet implemented') data = data[0] alldata, _ = makeDataset_sweep(data, self.sweepparams, self.sweepranges, gates=self.station.gates, loc_record={'label': 'videomode_1d_single'}) alldata.metadata = metadata elif data.ndim == 3: if (data.shape[0] > 1): warnings.warn('getting dataset for multiple dimensions not yet tested') import copy alldata = [None] * len(data) for jj in range(len(data)): datax = data[jj] alldatax, _ = makeDataset_sweep_2D(datax, self.station.gates, self.sweepparams, self.sweepranges, loc_record={ 'label': 'videomode_2d_single'}) alldatax.metadata = copy.copy(metadata) alldata[jj] = alldatax else: raise Exception('makeDataset: data.ndim %d' % data.ndim) return alldata def _get_Naverage(self): return self._Naverage_val def _set_Naverage(self, value): self._Naverage_val = value self.datafunction.Naverage = value self.box.setValue(value) @staticmethod def all_instances(verbose=1): """ Return all VideoMode instances """ lst = qtt.pgeometry.list_objects(VideoMode, verbose=verbose) return lst @staticmethod def get_instance(idx): """ Return instance by index """ lst = VideoMode.all_instances(verbose=0) for l in lst: if l.videomode_index == idx: return l return None @staticmethod def stop_all_instances(): """ Stop readout on all all VideoMode instances """ lst = qtt.pgeometry.list_objects(VideoMode) for v in lst: v.stopreadout()