def calculate_tunneling_rate(runids, station=None, exp=None, plot=False): meas = Measurement(exp=exp, station=station) meas.register_parameter(station.n6705b.VTUN, ) meas.register_custom_parameter("Tunneling_Gradient", setpoints=(station.n6705b.VTUN, ), unit="V/S") meas.register_custom_parameter("runid") grads = [] tun_vs = [] for id in runids: data = load_by_id(id) sweep_data = data.get_parameter_data() x = sweep_data['dmm_VOUT']['time'] y = sweep_data['dmm_VOUT']['dmm_VOUT'] tun_v = sweep_data['n6705b_VTUN']['n6705b_VTUN'][0] # y = mx + b m, b = polyfit(x, y, 1) grads.append(m) tun_vs.append(tun_v) if plot: plt.figure() plt.plot(x, y) y2 = x * m + b plt.plot(x, y2) plt.ylabel("vout [V]") plt.xlabel("time [s]") plt.show() with meas.run() as datasaver: for m, tv, run in zip(grads, tun_vs, runids): datasaver.add_result((station.n6705b.VTUN, tv), ("Tunneling_Gradient", m), ("runid", run))
def setup_measurement(dataset: OldDataSet, exp: Optional['Experiment'] = None) -> Measurement: """ Register parameters for all :class:.`DataArrays` in a given QCoDeS legacy dataset This tries to infer the `name`, `label` and `unit` along with any `setpoints` for the given array. Args: dataset: Legacy dataset to register parameters from. exp: experiment that the legacy dataset should be bound to. If None the default experiment is used. See the docs of :class:`.Measurement` for more details. """ meas = Measurement(exp=exp) for arrayname, array in dataset.arrays.items(): if array.is_setpoint: setarrays = None else: setarrays = [setarray.array_id for setarray in array.set_arrays] meas.register_custom_parameter(name=array.array_id, label=array.label, unit=array.unit, setpoints=setarrays ) return meas
def IV_up_ithaco(station, voltages, stanford_gain_V, ithaco_gain_I): now = datetime.now() # dd/mm/YY H:M:S dt_string = now.strftime("%d/%m/%Y %H:%M:%S") print(dt_string) R_I = 1e4 #The value of the resistor used to measure the current print(f'Stanford Gain V ={stanford_gain_V}') print(f'Ithaco Gain I ={ithaco_gain_I}') print(f'Voltage Max V_max = {voltages[-1]}') int_time = 1 #Integration time of the dmm's station.dmm1.volt() station.dmm1.NPLC(int_time) station.dmm2.volt() station.dmm2.NPLC(int_time) print(f'Integration time = {int_time*0.02} s') station.yoko.output('off') station.yoko.source_mode("VOLT") station.yoko.output('on') station.yoko.voltage.step = 0.1e-6 station.yoko.voltage.inter_delay = 5e-4 meas = Measurement() meas.register_parameter(station.yoko.voltage) meas.register_parameter(station.dmm2.volt) meas.register_custom_parameter("Current", unit="A") meas.register_parameter(station.dmm1.volt, setpoints=("Current",)) with meas.run() as datasaver: for v in voltages: station.yoko.voltage(v) voltage_meas = station.dmm1.volt()/stanford_gain_V current_meas = -1*station.dmm2.volt()/ithaco_gain_I datasaver.add_result((station.yoko.voltage,v), (station.dmm2.volt,station.dmm2.volt()), ("Current",current_meas), (station.dmm1.volt,voltage_meas)) ID_exp = datasaver.run_id station.yoko.voltage(0) plot_by_id(ID_exp)
def inject_to_target(exp, station, fn_switch, events, target): """ open loop sweep of vref """ # Set the switch state to 'open' if not callable(fn_switch): raise ValueError("Expecting Switch Function") fn_switch("open") voltages = { "vdd": 0, "vfeedback": 0, "vdrain": 0, "vbias": 0, "vref": 0, "vtun": 2, "vbus": 2 } deps = [ station.b2962.IDRAIN, station.dmm.VOUT, station.n6705b.VBUS, station.n6705b.VFEEDBACK, station.n6705b.VREF, station.n6705b.VTUN, station.b2962.VDD, station.b2962.VDRAIN, station.yoko.VBIAS ] meas = Measurement(exp=exp, station=station) meas.register_custom_parameter("time", label="Time", unit="S") meas.register_custom_parameter("injection_target", label="Injection Target", unit="A") meas.add_before_run(lambda: setup(station, voltages), ()) meas.add_after_run(lambda: all_disable(station), ()) for p in deps: meas.register_parameter(p, setpoints=("time", )) with meas.run() as datasaver: time.sleep(1) start = time.time() events.fn_before() while (station.b2962.IDRAIN() > target): save_list = [] save_list.append(["time", time.time() - start]) save_list.append(["injection_target", target]) for p in deps: save_list.append([p, p.get()]) datasaver.add_result(*save_list) time.sleep(events.step) runid = datasaver.run_id return runid
def test_datasaver_foul_input(experiment): meas = Measurement() meas.register_custom_parameter('foul', label='something unnatural', unit='Fahrenheit') foul_stuff = [qc.Parameter('foul'), set((1, 2, 3))] with meas.run() as datasaver: for ft in foul_stuff: with pytest.raises(ValueError): datasaver.add_result(('foul', ft))
def test_run_loaded_experiment(): """ Test that we can resume a measurement after loading by name """ new_experiment("test", "test1") exp_loaded = load_experiment_by_name("test", "test1") meas = Measurement(exp=exp_loaded) meas.register_custom_parameter(name='dummy', paramtype='text') with meas.run(): pass with meas.run(): pass
def test_nested_measurement(bg_writing): meas1 = Measurement() meas1.register_custom_parameter('foo1') meas1.register_custom_parameter('bar1', setpoints=('foo1', )) meas2 = Measurement() meas2.register_custom_parameter('foo2') meas2.register_custom_parameter('bar2', setpoints=('foo2', )) with meas1.run(write_in_background=bg_writing) as ds1, \ meas2.run(write_in_background=bg_writing) as ds2: for i in range(10): ds1.add_result(("foo1", i), ("bar1", i**2)) ds2.add_result(("foo2", 2 * i), ("bar2", (2 * i)**2)) data1 = ds1.dataset.get_parameter_data()["bar1"] assert len(data1.keys()) == 2 assert "foo1" in data1.keys() assert "bar1" in data1.keys() assert_allclose(data1["foo1"], np.arange(10)) assert_allclose(data1["bar1"], np.arange(10)**2) data2 = ds2.dataset.get_parameter_data()["bar2"] assert len(data2.keys()) == 2 assert "foo2" in data2.keys() assert "bar2" in data2.keys() assert_allclose(data2["foo2"], np.arange(0, 20, 2)) assert_allclose(data2["bar2"], np.arange(0, 20, 2)**2)
def setup_measurement(dataset: OldDataSet) -> Measurement: """ Register parameters for all DataArrays in a given QCoDeS legacy dataset This tries to infer the name, label and unit along with any setpoints for the given array. """ meas = Measurement() for arrayname, array in dataset.arrays.items(): if array.is_setpoint: setarrays = None else: setarrays = [setarray.array_id for setarray in array.set_arrays] meas.register_custom_parameter(name=array.array_id, label=array.label, unit=array.unit, setpoints=setarrays) return meas
def tunnel_to_target(exp, station, fn_switch, voltages, vtun, target): """ open loop sweep of vref """ # Set the switch state to 'open' if not callable(fn_switch): raise ValueError("Expecting Switch Function") fn_switch("close") deps = [ station.dmm.VOUT, station.n6705b.VBUS, station.n6705b.VFEEDBACK, station.n6705b.VREF, station.n6705b.VTUN, station.b2962.VDD, station.b2962.VDRAIN, station.yoko.VBIAS ] meas = Measurement(exp=exp, station=station) meas.register_custom_parameter("time", label="Time", unit="S") meas.register_custom_parameter("tunnel_target", label="Tunnel Target", unit="V") meas.add_before_run(lambda: setup(station, voltages), ()) meas.add_after_run(lambda: all_disable(station), ()) for p in deps: meas.register_parameter(p, setpoints=("time", )) voltages['vtun'] = vtun with meas.run() as datasaver: time.sleep(1) start = time.time() print(station.dmm.VOUT()) while (station.dmm.VOUT() > target): save_list = [] save_list.append(["time", time.time() - start]) save_list.append(["tunnel_target", target]) for p in deps: save_list.append([p, p.get()]) datasaver.add_result(*save_list) time.sleep(0.1) runid = datasaver.run_id return runid
def _create_measurement(self, *set_params): meas = Measurement() for p in set_params: meas.register_parameter(p) meas.register_custom_parameter('time', label='Time', unit='s') for p in self._params: meas.register_parameter(p, setpoints=( *set_params, 'time', )) for l, _, _ in self._sr830s: meas.register_parameter(l.X, setpoints=( *set_params, 'time', )) meas.register_parameter(l.Y, setpoints=( *set_params, 'time', )) return meas
def time_sweep(dependents, event_list, fn_before=None, fn_after=None, exp=None, station=None): """ Takes a list of Events which can run some event and then measure for a period of time. Eg Set vtun to 3v for 5 secs, then bump to 10v for 2 secs, then back to 3v for another 5 secs. """ meas = Measurement(exp=exp, station=station) meas.register_custom_parameter("time", label="Time", unit="S") if callable(fn_before): meas.add_before_run(fn_before, ()) if callable(fn_after): meas.add_after_run(fn_after, ()) for p in dependents: meas.register_parameter(p, setpoints=("time", )) with meas.run() as datasaver: start = time.time() for event in event_list: begin = time.time() if callable(event.fn_before): event.fn_before() time.sleep(0.5) while (time.time() - begin < event.time): save_list = [] save_list.append(["time", time.time() - start]) for p in dependents: save_list.append([p, p.get()]) datasaver.add_result(*save_list) time.sleep(event.step) runid = datasaver.run_id return runid
def test_snapshot_creation_for_types_not_supported_by_builtin_json(experiment): """ Test that `Measurement`/`Runner`/`DataSaver` infrastructure successfully dumps station snapshots in JSON format in cases when the snapshot contains data of types that are not supported by python builtin `json` module, for example, numpy scalars. """ p1 = ManualParameter('p_np_int32', initial_value=numpy.int32(5)) p2 = ManualParameter('p_np_float16', initial_value=numpy.float16(5.0)) p3 = ManualParameter('p_np_array', initial_value=numpy.meshgrid((1, 2), (3, 4))) p4 = ManualParameter('p_np_bool', initial_value=numpy.bool_(False)) station = Station(p1, p2, p3, p4) measurement = Measurement(experiment, station) # we need at least 1 parameter to be able to run the measurement measurement.register_custom_parameter('dummy') with measurement.run() as data_saver: # we do this in order to create a snapshot of the station and add it # to the database pass snapshot = data_saver.dataset.snapshot assert 5 == snapshot['station']['parameters']['p_np_int32']['value'] assert 5 == snapshot['station']['parameters']['p_np_int32']['raw_value'] assert 5.0 == snapshot['station']['parameters']['p_np_float16']['value'] assert 5.0 == snapshot['station']['parameters']['p_np_float16'][ 'raw_value'] lst = [[[1, 2], [1, 2]], [[3, 3], [4, 4]]] assert lst == snapshot['station']['parameters']['p_np_array']['value'] assert lst == snapshot['station']['parameters']['p_np_array']['raw_value'] assert False is snapshot['station']['parameters']['p_np_bool']['value'] assert False is snapshot['station']['parameters']['p_np_bool']['raw_value']
def array_dataset_with_nulls(experiment, request): """ A dataset where two arrays are measured, one as a function of two other (setpoint) arrays, the other as a function of just one of them """ meas = Measurement() meas.register_custom_parameter('sp1', paramtype=request.param) meas.register_custom_parameter('sp2', paramtype=request.param) meas.register_custom_parameter('val1', paramtype=request.param, setpoints=('sp1', 'sp2')) meas.register_custom_parameter('val2', paramtype=request.param, setpoints=('sp1', )) with meas.run() as datasaver: sp1_vals = np.arange(0, 5) sp2_vals = np.arange(5, 10) val1_vals = np.ones(5) val2_vals = np.zeros(5) datasaver.add_result(('sp1', sp1_vals), ('sp2', sp2_vals), ('val1', val1_vals)) datasaver.add_result(('sp1', sp1_vals), ('val2', val2_vals)) try: yield datasaver.dataset finally: datasaver.dataset.conn.close()
def save_1Ddata_with_qcodes(data_generator_method, metadata_generator_method): meas = Measurement() meas.register_custom_parameter("voltage", paramtype="numeric", label="voltage x", unit="V") meas.register_custom_parameter( "current", paramtype="numeric", label="dc current", unit="A", setpoints=("voltage", ), ) meas.register_custom_parameter( "sensor", paramtype="numeric", label="dc sensor", unit="A", setpoints=("voltage", ), ) voltage, current, sensor = data_generator_method() with meas.run() as datasaver: datasaver.add_result(("voltage", voltage), ("current", current)) datasaver.add_result(("voltage", voltage), ("sensor", sensor)) nt_metadata, current_label = metadata_generator_method() datasaver.dataset.add_metadata(nt.meta_tag, json.dumps(nt_metadata)) datasaver.dataset.add_metadata("snapshot", json.dumps({})) for label, value in current_label.items(): datasaver.dataset.add_metadata(label, value) return datasaver
def test_datasaver_unsized_arrays(empty_temp_db, N): new_experiment('firstexp', sample_name='no sample') meas = Measurement() meas.register_custom_parameter(name='freqax', label='Frequency axis', unit='Hz') meas.register_custom_parameter(name='signal', label='qubit signal', unit='Majorana number', setpoints=('freqax', )) # note that np.array(some_number) is not the same as the number # its also not an array with a shape. Check here that we handle it # correctly with meas.run() as datasaver: freqax = np.linspace(1e6, 2e6, N) signal = np.random.randn(N) for i in range(N): myfreq = np.array(freqax[i]) mysignal = np.array(signal[i]) datasaver.add_result(('freqax', myfreq), ('signal', mysignal)) assert datasaver.points_written == N
def test_datasaver_arrays(empty_temp_db, N): new_experiment('firstexp', sample_name='no sample') meas = Measurement() meas.register_custom_parameter(name='freqax', label='Frequency axis', unit='Hz') meas.register_custom_parameter(name='signal', label='qubit signal', unit='Majorana number', setpoints=('freqax',)) with meas.run() as datasaver: freqax = np.linspace(1e6, 2e6, N) signal = np.random.randn(N) datasaver.add_result(('freqax', freqax), ('signal', signal)) assert datasaver.points_written == N with meas.run() as datasaver: freqax = np.linspace(1e6, 2e6, N) signal = np.random.randn(N-1) with pytest.raises(ValueError): datasaver.add_result(('freqax', freqax), ('signal', signal)) meas.register_custom_parameter(name='gate_voltage', label='Gate tuning potential', unit='V') meas.register_custom_parameter(name='signal', label='qubit signal', unit='Majorana flux', setpoints=('freqax', 'gate_voltage')) with meas.run() as datasaver: freqax = np.linspace(1e6, 2e6, N) signal = np.random.randn(N) datasaver.add_result(('freqax', freqax), ('signal', signal), ('gate_voltage', 0)) assert datasaver.points_written == N
def test_register_custom_parameter(DAC): """ Test the registration of custom parameters """ meas = Measurement() name = 'V_modified' unit = 'V^2' label = 'square of the voltage' meas.register_custom_parameter(name, label, unit) assert len(meas.parameters) == 1 assert isinstance(meas.parameters[name], ParamSpec) assert meas.parameters[name].unit == unit assert meas.parameters[name].label == label assert meas.parameters[name].type == 'numeric' newunit = 'V^3' newlabel = 'cube of the voltage' meas.register_custom_parameter(name, newlabel, newunit) assert len(meas.parameters) == 1 assert isinstance(meas.parameters[name], ParamSpec) assert meas.parameters[name].unit == newunit assert meas.parameters[name].label == newlabel with pytest.raises(ValueError): meas.register_custom_parameter(name, label, unit, setpoints=(DAC.ch1, )) with pytest.raises(ValueError): meas.register_custom_parameter(name, label, unit, basis=(DAC.ch2, )) meas.register_parameter(DAC.ch1) meas.register_parameter(DAC.ch2) meas.register_custom_parameter('strange_dac') meas.register_custom_parameter(name, label, unit, setpoints=(DAC.ch1, str(DAC.ch2)), basis=('strange_dac', )) assert len(meas.parameters) == 4 parspec = meas.parameters[name] assert parspec.inferred_from == 'strange_dac' assert parspec.depends_on == ', '.join([str(DAC.ch1), str(DAC.ch2)]) with pytest.raises(ValueError): meas.register_custom_parameter('double dependence', 'label', 'unit', setpoints=(name, ))
def test_nested_measurement_array(bg_writing, outer_len, inner_len1, inner_len2): meas1 = Measurement() meas1.register_custom_parameter('foo1', paramtype='numeric') meas1.register_custom_parameter('bar1spt', paramtype='array') meas1.register_custom_parameter('bar1', setpoints=('foo1', "bar1spt"), paramtype='array') meas2 = Measurement() meas2.register_custom_parameter('foo2', paramtype='numeric') meas2.register_custom_parameter('bar2spt', paramtype='array') meas2.register_custom_parameter('bar2', setpoints=( 'foo2', 'bar2spt', ), paramtype='array') with meas1.run(write_in_background=bg_writing) as ds1, \ meas2.run(write_in_background=bg_writing) as ds2: for i in range(outer_len): bar1sptdata = np.arange(inner_len1) bar2sptdata = np.arange(inner_len2) ds1.add_result(("foo1", i), ("bar1spt", bar1sptdata), ("bar1", np.ones(inner_len1) * i * bar1sptdata)) ds2.add_result(("foo2", i), ("bar2spt", bar2sptdata), ("bar2", np.ones(inner_len2) * i * bar2sptdata)) data1 = ds1.dataset.get_parameter_data()["bar1"] assert len(data1.keys()) == 3 assert "foo1" in data1.keys() assert "bar1spt" in data1.keys() assert "bar1" in data1.keys() expected_foo1_data = np.repeat(np.arange(outer_len), inner_len1).reshape(outer_len, inner_len1) expected_bar1spt_data = np.tile(np.arange(inner_len1), (outer_len, 1)) assert_allclose(data1["foo1"], expected_foo1_data) assert_allclose(data1["bar1spt"], expected_bar1spt_data) assert_allclose(data1["bar1"], expected_foo1_data * expected_bar1spt_data) data2 = ds2.dataset.get_parameter_data()["bar2"] assert len(data2.keys()) == 3 assert "foo2" in data2.keys() assert "bar2spt" in data2.keys() assert "bar2" in data2.keys() expected_foo2_data = np.repeat(np.arange(outer_len), inner_len2).reshape(outer_len, inner_len2) expected_bar2spt_data = np.tile(np.arange(inner_len2), (outer_len, 1)) assert_allclose(data2["foo2"], expected_foo2_data) assert_allclose(data2["bar2spt"], expected_bar2spt_data) assert_allclose(data2["bar2"], expected_foo2_data * expected_bar2spt_data)
def G_up(station, v_gates, v_polar, amplitude, stanford_gain_V_ac): #Before using this code change these values according to your own setup : R_polar = 1e6 #value of the polarization resistor now = datetime.now() dt_string = now.strftime("%d/%m/%Y %H:%M:%S") # dd/mm/YY H:M:S print(dt_string) #print date and time of the measurement #Start the measurement meas = Measurement() meas.register_parameter(station.lockin_2.amplitude) meas.register_parameter(station.lockin_2.sine_outdc) meas.register_parameter(station.mdac_8.ch01.voltage) meas.register_parameter(station.lockin_1.Y, setpoints=(station.mdac_8.ch01.voltage, )) meas.register_parameter(station.lockin_2.Y, setpoints=(station.mdac_8.ch01.voltage, )) meas.register_parameter(station.lockin_1.X, setpoints=(station.mdac_8.ch01.voltage, )) meas.register_parameter(station.lockin_2.X, setpoints=(station.mdac_8.ch01.voltage, )) meas.register_custom_parameter("R_ac", unit="Ohm", setpoints=(station.mdac_8.ch01.voltage, )) #Prepare the live plot win = qcm.pyplot.PlotWindow(title="Gate Sweep 1D") win.resize(600, 400) plot = win.addPlot(title="R_ac(V_g)") plot.update_axes(station.mdac_8.ch01.voltage, station.lockin_1.X) R_ac_all = np.full(len(v_gates), np.nan) #Print the main lockin settings print(f'Stanford Gain V_AC ={stanford_gain_V_ac}') time_constant = station.lockin_2.time_constant() print(f'Integration time lockins {time_constant} s') print(f'Frequency Lockin : {station.lockin_2.frequency()} Hz') print(f'Filter lockin 1 : {station.lockin_1.filter_slope()} dB roll off') print(f'Sensitivity lockin 1 : {station.lockin_1.sensitivity()} V') print(f'Filter lockin 2 : {station.lockin_2.filter_slope()} dB roll off') print(f'Sensitivity lockin 2 : {station.lockin_2.sensitivity()} A') #Initialisation of the lockin station.lockin_2.amplitude(amplitude) print(f'V_ac polarization : {amplitude/1e-3} mV') station.lockin_2.sine_outdc(v_polar) print(f'V_dc polarization : {v_polar/1e-3} mV') with meas.run() as datasaver: for i, v_g in enumerate(v_gates): station.mdac_8.ch01.ramp(v_g, 0.01) station.mdac_8.ch02.ramp(v_g, 0.01) station.mdac_8.ch03.ramp(v_g, 0.01) station.mdac_8.ch01.block() station.mdac_8.ch02.block() station.mdac_8.ch03.block() print(v_g) time.sleep(5 * time_constant) voltage_X_AC = station.lockin_1.X() / stanford_gain_V_ac current_X_AC = station.lockin_2.X() voltage_Y_AC = station.lockin_1.Y() / stanford_gain_V_ac current_Y_AC = station.lockin_2.Y() R_ac = voltage_X_AC / current_X_AC R_ac_all[i] = R_ac trace = plot.plot(setpoint_x=v_gates, pen=(0, 0, 255), name="R_ac") trace.update(R_ac_all) datasaver.add_result(("R_ac", R_ac), (station.lockin_2.amplitude, amplitude), (station.lockin_2.sine_outdc, v_polar), (station.mdac_8.ch01.voltage, v_g), (station.lockin_2.Y, current_Y_AC), (station.lockin_1.Y, voltage_Y_AC), (station.lockin_2.X, current_X_AC), (station.lockin_1.X, voltage_X_AC)) ID_exp = datasaver.run_id station.lockin_2.sine_outdc(0) station.lockin_2.amplitude(0) win.export('figures/Rac_Gate_sweep_1D_ID_exp_' + str(ID_exp) + '.png') plot_by_id(ID_exp)
def Hall_gate(station, v_gates, V_polar, field_range_Y, stanford_gain_1, stanford_gain_2, stanford_gain_3): R_polar = 1e6 print(f'I_polar = {V_polar/R_polar}') R_I = 1e4 # dmm1 is voltage station.dmm1.NPLC(100) # dmm2 is current station.dmm2.NPLC(10) station.dmm3.NPLC(100) station.yoko.voltage.step = 1e-4 station.yoko.voltage.inter_delay = 0.0001 meas = Measurement() meas.register_parameter(station.yoko.voltage) meas.register_parameter(station.mag.y_target) meas.register_parameter(station.mag.y_measured) meas.register_parameter(station.mdac_8.ch01.voltage) meas.register_parameter(station.dmm1.volt, setpoints = (station.mag.y_target,station.mdac_8.ch01.voltage)) meas.register_parameter(station.dmm2.volt, setpoints = (station.mag.y_target,station.mdac_8.ch01.voltage)) meas.register_parameter(station.dmm3.volt, setpoints = (station.mag.y_target,station.mdac_8.ch01.voltage)) meas.register_custom_parameter("R_h", unit = "Ohms", setpoints = (station.mag.y_target,station.mdac_8.ch01.voltage)) meas.register_custom_parameter("R_xx", unit = "Ohms", setpoints = (station.mag.y_target,station.mdac_8.ch01.voltage)) with meas.run() as datasaver: for b in field_range_Y: station.mag.y_target(b) station.mag.ramp('simul') while abs(station.mag.y_measured()-b)>0.001: time.sleep(2) time.sleep(5) l_y = station.mag.y_measured() print(l_y) for v_g in v_gates: station.mdac_8.ch01.ramp(v_g, 0.01) while abs(station.mdac_8.ch01.voltage()-v_g)>0.001: time.sleep(2) time.sleep(2) print(f'V_g = {v_g} V') station.yoko.voltage(-V_polar) time.sleep(1) volt_h_m = station.dmm1.volt()/stanford_gain_1 curr_m = station.dmm2.volt()/(R_I*stanford_gain_2) volt_m = station.dmm3.volt()/stanford_gain_3 time.sleep(1) station.yoko.voltage(V_polar) time.sleep(1) volt_h_p = station.dmm1.volt()/stanford_gain_1 curr_p = station.dmm2.volt()/(R_I*stanford_gain_2) volt_p = station.dmm3.volt()/stanford_gain_3 time.sleep(1) V_av = (volt_p - volt_m)/2 I_av = (curr_p - curr_m)/2 V_h_av = (volt_h_p - volt_h_m)/2 R_av = V_av/I_av R_h_av = V_h_av/I_av print(R_av) datasaver.add_result((station.mdac_8.ch01.voltage, v_g), (station.yoko.voltage, V_polar), (station.dmm2.volt, curr_p), (station.dmm1.volt, V_h_av), (station.dmm3.volt, V_av), (station.mag.y_measured, l_y), (station.mag.y_target, b), ("R_h", R_h_av), ("R_xx",R_av)) station.yoko.voltage(0) ID_exp = datasaver.run_id plot_by_id(ID_exp)
class BaseSweep(QObject): """ This is the base class for the 0D (tracking) sweep class and the 1D sweep class. Each of these functions is used by both classes. """ start_sweep = pyqtSignal() def __init__(self, set_param=None, inter_delay=0.01, save_data=True, plot_data=True, x_axis=1, datasaver=None): """ Initializer for both classes, called by super().__init__() in Sweep0D and Sweep1D classes. Simply initializes the variables and flags. Arguments: set_param - QCoDeS Parameter to be swept inter_delay - Time (in seconds) to wait between data points save_data - Flag used to determine if the data should be saved or not plot_data - Flag to determine if we should live-plot data """ self._params = [] self._srs = [] self.set_param = set_param self.inter_delay = inter_delay self.save_data = save_data self.plot_data = plot_data self.x_axis = x_axis self.is_running = False self.t0 = time.monotonic() self.runnerThread = QThread() self.plotterThread = QThread() self.datasaver = datasaver QObject.__init__(self) def follow_param(self, *p): """ This function saves parameters to be tracked, for both saving and plotting data. The parameters must be followed before '_create_measurement()' is called. Arguments: *p - Variable number of arguments, each of which must be a QCoDeS Parameter that you want the sweep to follow """ for param in p: if isinstance(param, list): for l in param: self._params.append(l) else: self._params.append(param) def follow_srs(self, l, name, gain=1.0): """ Adds an SRS lock-in to ensure that the range is kept correctly. Arguments: l - lockin instrument name - name of instrument gain - current gain value """ self._srs.append((l, name, gain)) def _create_measurement(self): """ Creates a QCoDeS Measurement object. This controls the saving of data by registering QCoDeS Parameter objects, which this function does. Registers all 'tracked' parameters, Returns the measurement object. This function will register only parameters that are followed BEFORE this function is called. """ # First, create time parameter self.meas = Measurement() self.meas.register_custom_parameter('time', label='Time', unit='s') # Check if we are 'setting' a parameter, and register it if self.set_param is not None: self.meas.register_parameter(self.set_param) # Register all parameters we are following for p in self._params: self.meas.register_parameter(p) return self.meas def stop(self): """ Stops/pauses the program from running by setting the 'is_running' flag to false. This is the flag that the children threads check in their loop to determine if they should continue running. """ self.is_running = False def check_running(self): """ Returns the status of the sweep. """ return self.is_running def start(self, persist_data=None): """ Starts the sweep by creating and running the worker threads. Used to both start the program and unpause after calling 'stop()' """ # Flag that we are now running. self.is_running = True first = False if self.plotter is None and self.runner is None: first = True # Save persistent data from 2D sweep self.persist_data = persist_data # If we don't have a plotter yet want to plot, create it and the figures if self.plotter is None and self.plot_data is True: self.plotter = Plotter(self) self.plotter.create_figs() # If we don't have a runner, create it and tell it of the plotter, # which is where it will send data to be plotted if self.runner is None: self.runner = Runner(self) self.datasaver = self.runner.datasaver self.runner.add_plotter(self.plotter) if first is True: # Tells the threads to begin self.runner.moveToThread(self.runnerThread) self.plotter.moveToThread(self.plotterThread) self.runnerThread.start() self.plotterThread.start() self.start_sweep.connect(self.runner.run) self.start_sweep.emit() def update_values(self): """ Iterates our data points, changing our setpoint if we are sweeping, and refreshing the values of all our followed parameters. If we are saving data, it happens here, and the data is returned. Returns: data - A list of tuples with the new data. Each tuple is of the format (<QCoDeS Parameter>, measurement value). The tuples are passed in order of time, then set_param (if applicable), then all the followed params. """ t = time.monotonic() - self.t0 data = [] data.append(('time', t)) if self.set_param is not None: data.append(self.step_param()) persist_param = None if self.persist_data is not None: data.append(self.persist_data) persist_param = self.persist_data[0] for i, (l, _, gain) in enumerate(self._srs): _autorange_srs(l, 3) for i, p in enumerate(self._params): if p is not persist_param: v = p.get() data.append((p, v)) if self.save_data and self.is_running: self.runner.datasaver.add_result(*data) return data def clear_plot(self): """ Clears the currently showing plots. """ self.plotter.reset() def no_change(self, *args): """ This function is passed when we don't need to connect a function when the sweep is completed. """ pass def __del__(self): if self.datasaver is not None: self.datasaver.__exit__()
def _create_measurement(self, *set_params): meas = Measurement() self._etp = ElapsedTimeParameter('time') meas.register_parameter(self._etp) for param in set_params: meas.register_parameter(param) for param in self._params: meas.register_parameter(param.param, setpoints=(*set_params, self._etp)) for sr830 in self._sr830s: meas.register_parameter(sr830.sr830.X, setpoints=(*set_params, self._etp)) meas.register_parameter(sr830.sr830.Y, setpoints=(*set_params, self._etp)) if self._fbl is not None: for c in self._fbl.channels: meas.register_custom_parameter(f'fbl_c{c}_x', setpoints=(*set_params, self._etp)) meas.register_custom_parameter(f'fbl_c{c}_p', setpoints=(*set_params, self._etp)) if self._fbl.outputs: meas.register_custom_parameter(f'fbl_c{c}_o', setpoints=(*set_params, self._etp)) if self._fbl.setpoints: meas.register_custom_parameter(f'fbl_c{c}_s', setpoints=(*set_params, self._etp)) if self._fbl.dc: meas.register_custom_parameter(f'fbl_c{c}_dc', setpoints=(*set_params, self._etp)) for fn in self._fns: meas.register_custom_parameter(fn.name, setpoints=(*set_params, self._etp)) return meas
def sweep_time(*param_meas, delay=10, until=None, win=None, append=False, plot_params=None, annotation=None, atstart=(), ateach=(), atend=()): """ Run a time sweep, with a delay between each point. This sweep will run for `until` seconds, or indefinitely if until is None Args: *param_meas (Iterable[Parameter]): A list of the parameters to be measured at each of the set points. For now, these MUST be simple parameters. Arrays cannot be measured. win (Optional[PlotWindow]): The plot window to add plots to. If this value is None, the sweep will not be live plotted. append (bool): If this parameter is true, the trace will be appended to an existing window. plot_params (Optional[Iterable[Parameter]]): A list of parameters to plot. If not passed or None, all measured parameters will be automatically plotted. atstart (Optional[Union[Callable,Iterable[Callable]]]): A function or list of functions to be run before the measurement is started. The functions will be run BEFORE the parameters are inserted into the measurement, hence if some parameters require setup before they are run, they can be inserted here. ateach (Optional[Union[Callable,Iterable[Callable]]]): A function or list of functions to be run after each time the sweep parameter is set. These functions will be run AFTER the delay, and so is suitable if an instrument requires a call to capture a trace before the parameter can be read. atend (Optional[Union[Callable,Iterable[Callable]]]): A function or list of functions to be run at the end of a trace. This is run AFTER the data is saved into the database, and after parameters are set back to their starting points (if setback is True), and can therefore be used to read the data that was taken and potentially do some post analysis. Returns: (iw, win): ID is the trace id of the saved wave, win is a handle to the plot window that was created for the purposes of liveplotting. """ _flush_buffers(*param_meas) # Register setpoints m = Measurement() m.register_custom_parameter("time", label="Time", unit="s") _run_functions(atstart) # Keep track of data and plots plt_data = {} time_data = np.full((1,), np.nan) array_size = 1 curr_point = 0 # If plot_params is not given, plot all measured parameters if plot_params is None: plot_params = param_meas # Set up parameters for param in param_meas: m.register_parameter(param, setpoints=("time", )) # Create plot window if win is not None and param in plot_params: plot = win.addPlot(name=param.full_name, title=f"{param.full_name} ({param.label})") plot.left_axis.label = param.label plot.left_axis.unit = param.unit plot.bot_axis.label = "Time" plot.bot_axis.unit = "s" plotdata = plot.plot(setpoint_x=time_data, name=param.name, pen=(255,0,0)) plt_data[param] = (plot, plotdata, np.full((1,), np.nan)) if win is not None and annotation is not None: win.items[0].textbox(annotation) try: with m.run() as datasaver: start_time = time.monotonic() win.win_title += f"{datasaver.run_id}" for pd in plt_data.values(): pd[0].plot_title += f" (id: {datasaver.run_id})" while True: # Update each parameter data = [("time", time.monotonic()-start_time)] time_data[curr_point] = data[-1][1] _run_functions(ateach, param_vals=(Setpoint("time", curr_point, data[-1][1]))) if until is not None and time_data[curr_point] > until: break for param in param_meas: val = param() if val is None: val = np.nan data.append((param, val)) if param in plot_params: plt_data[param][2][curr_point] = data[-1][1] plt_data[param][1].xData = time_data plt_data[param][1].update(plt_data[param][2]) curr_point += 1 # Resize plot arrays if necessary if array_size == curr_point: array_size *= 2 logger.debug("New plot array size: %d", array_size) time_data.resize(array_size) time_data[array_size//2:] = np.nan for pld in plt_data.values(): pld[2].resize(array_size) pld[2][array_size//2:] = np.nan datasaver.add_result(*data) # Wait until the next point time. Try to keep track of how long it # took for equipment to respond next_time = start_time + delay*curr_point while time.monotonic() < next_time: sleep_time = max(0, min(0.01, time.monotonic() - next_time)) time.sleep(sleep_time) except KeyboardInterrupt: print(f"Trace cancelled with Ctrl-C") print(f"Ending plot at time {time.monotonic() - start_time}.") finally: _run_functions(atend) return datasaver.run_id
class Sweep1D(object): """ Class to control sweeping along 1 parameter, while tracking multiple other parameters. It has functionality to live plot, or not, and one can create their own figures (e.g. in a GUI) or have the class create its own matplotlib figues. Follows QCoDeS data-taking methods. Adapted from Joe Finney's code. SR830s are not currently implemented. """ def __init__(self, set_param, start, stop, step, freq, bidirectional=False, meas=None, plot=False, auto_figs=False): """ Initializes the Sweep object. Takes in the parameter to be swept (set_param), the value to start and stop sweeping at (start/stop, respectively), the step spacing (step), and the frequency of measurements. Can turn plotting off through 'plot', also tells system whether to create it's own plots or use given ones through 'auto_figs'. """ # Save our input variables self.set_param = set_param self.start = start self.stop = stop self.step = step if (self.stop - self.start) > 0: self.step = abs(self.step) else: self.step = (-1) * abs(self.step) self.inter_delay = 1 / freq self.t0 = time.monotonic() self.setpoint = self.start - self.step self.bidirectional = bidirectional self.direction = 0 #Forward d = (stop - start) / step * self.inter_delay h, m, s = int(d / 3600), int(d / 60) % 60, int(d) % 60 print(f'Minimum duration: {h}h {m}m {s}s') # Either mark or save the measurement object if meas is not None: self.meas = meas # Saves our plotting flags self.plot = plot self.auto_figs = auto_figs # Sets a flag to ensure that the figures have been created before trying to plot self.figs_set = False self.pause = False self._sr830s = [] self._params = [] def follow_param(self, p): """ This function takes in a QCoDeS Parameter p, and tracks it through each sweep. """ self._params.append(p) def follow_sr830(self, l, name, gain=1.0): """ This function adds an SR830, but (as of now) does not do anything with it. """ self._sr830s.append((l, name, gain)) def _create_measurement(self, *set_params): """ Creates a QCoDeS Measurement object. This controls the saving of data by registering QCoDeS Parameter objects, which this function does. Registers all 'sweeping' parameters (set_params), and all 'tracked' parameters, 'self._params'. Returns the measurement object. """ self.meas = Measurement() for p in set_params: self.meas.register_parameter(p) self.meas.register_custom_parameter('time', label='Time', unit='s') for p in self._params: self.meas.register_parameter(p, setpoints=( *set_params, 'time', )) for l, _, _ in self._sr830s: self.meas.register_parameter(l.X, setpoints=( *set_params, 'time', )) self.meas.register_parameter(l.Y, setpoints=( *set_params, 'time', )) return self.meas def create_figs(self): """ Creates default figures for each of the parameters. Plots them in a new, separate window. """ self.fig = plt.figure( figsize=(4 * (2 + len(self._params) + len(self._sr830s)), 4)) self.grid = plt.GridSpec(4, 1 + len(self._params) + len(self._sr830s), hspace=0) self.setax = self.fig.add_subplot(self.grid[:, 0]) # First, create a plot of the sweeping parameters value against time self.setax.set_xlabel('Time (s)') self.setax.set_ylabel( f'{self.set_param.label} ({self.set_param.unit})') self.setaxline = self.setax.plot([], [])[0] self.plines = [] self.axes = [] # Now create a plot for every tracked parameter as a function of sweeping parameter for i, p in enumerate(self._params): self.axes.append(self.fig.add_subplot(self.grid[:, 1 + i])) self.axes[i].set_xlabel( f'{self.set_param.label} ({self.set_param.unit})') self.axes[i].set_ylabel(f'{p.label} ({p.unit})') forward_line = matplotlib.lines.Line2D([], []) forward_line.set_color('b') backward_line = matplotlib.lines.Line2D([], []) backward_line.set_color('r') self.axes[i].add_line(forward_line) self.axes[i].add_line(backward_line) self.plines.append((forward_line, backward_line)) self.figs_set = True def set_figs(self, fig, setax, axes): """ Give a figure and plots for both the sweeping parameter and the tracked parameters for the program to update. fig is of type matplotlib Figure, setax is a (sub)plot, and axes is an array of subplots. """ self.figs_set = True self.fig = fig self.setax = setax self.axes = axes # Initializes sweeping plot self.setax.set_xlabel('Time (s)') self.setax.set_ylabel( f'{self.set_param.label} ({self.set_param.unit})') self.setaxline = setax.plot([], [])[0] self.plines = [] # Initializes tracking plots for i, p in enumerate(self._params): self.axes[i].set_xlabel( f'{self.set_param.label} ({self.set_param.unit})') self.axes[i].set_ylabel(f'{p.label} ({p.unit})') forward_line = matplotlib.lines.Line2D([], []) forward_line.set_color('b') backward_line = matplotlib.lines.Line2D([], []) backward_line.set_color('r') self.axes[i].add_line(forward_line) self.axes[i].add_line(backward_line) self.plines.append((forward_line, backward_line)) def autorun(self, datasaver=None, persist_data=None): """ Run a sweep through this class. Makes call to create_figs if needed. Calls self.iterate to move through each data point. """ # Checks to see if it needs to generate its own figures if self.plot and self.auto_figs and not self.figs_set: self.create_figs() # If plots should have been set but are not, return 0 if self.plot is True and self.figs_set is False: return 0 # Run the loop if datasaver is None: with self.meas.run() as datasaver: # Check if we are within the stopping condition while abs(self.setpoint - self.stop) > abs(self.step / 2): self.iterate(datasaver) # If we want to go both ways, we flip the start and stop, and run again if self.bidirectional: self.flip_direction() while abs(self.setpoint - self.stop) > abs(self.step / 2): self.iterate(datasaver) self.flip_direction() else: # Check if we are within the stopping condition while abs(self.setpoint - self.stop) > abs(self.step / 2): self.iterate(datasaver, persist_data) # If we want to go both ways, we flip the start and stop, and run again if self.bidirectional: self.flip_direction() while abs(self.setpoint - self.stop) > abs(self.step / 2): self.iterate(datasaver, persist_data) self.flip_direction() return 1 def iterate(self, datasaver=None, persist_data=None): """ Runs one 'step' in the sweep. Takes in only the datasaver object, which is always a Measurement object's run() function (see autorun()). Iterate will update the sweeping parameter, read each of the tracking parameters, and update the plots if plotting is enabled. Returns all values as a list of tuples, in the form of (parameter_name, parameter_value). """ t = time.monotonic() - self.t0 # Step the setpoint, and update the value self.setpoint = self.step + self.setpoint self.set_param.set(self.setpoint) # Update the sweeping parameter plot if self.plot is True: self.setaxline.set_xdata(np.append(self.setaxline.get_xdata(), t)) self.setaxline.set_ydata( np.append(self.setaxline.get_ydata(), self.setpoint)) self.setax.relim() self.setax.autoscale_view() # Pause if desired if self.inter_delay is not None: time.sleep(self.inter_delay) # Create our data storage object, which is a list of tuples of the parameter # and its value data = [] if persist_data is not None: data.append(persist_data) data.append((self.set_param, self.setpoint)) data.append(('time', t)) # Loop through each of the tracking parameters for i, p in enumerate(self._params): # Update their values, and add them to the data object v = p.get() data.append((p, v)) # Update each of the plots for the tracking parameters if self.plot is True: self.plines[i][self.direction].set_xdata( np.append(self.plines[i][self.direction].get_xdata(), self.setpoint)) self.plines[i][self.direction].set_ydata( np.append(self.plines[i][self.direction].get_ydata(), v)) self.axes[i].relim() self.axes[i].autoscale_view() # Add this point to the dataset if datasaver is not None: datasaver.add_result(*data) # Set the plots if self.plot is True: self.fig.tight_layout() self.fig.canvas.draw() plt.pause(0.001) # Finally return all data return data def pause_run(self): self.pause = not self.pause def is_paused(self): return self.pause def ramp_to_zero(self): self.stop = 0 if self.setpoint - self.step > 0: self.step = (-1) * abs(self.step) else: self.step = abs(self.step) print(f'Ramping {self.set_param.label} to 0 . . . ') while abs(self.setpoint - self.stop) > abs(self.step / 2): self.iterate() self.set_param.set(0) print(f'Done ramping {self.set_param.label} to 0!') def flip_direction(self): """ Flips the direction of the sweep, to do bidirectional sweeping. """ temp = self.start self.start = self.stop self.stop = temp self.step = -1 * self.step self.setpoint -= self.step # If backwards, go forwards, and vice versa if self.direction: self.direction = 0 else: self.direction = 1 def reset(self, new_params=None): """ Resets the Sweep1D to reuse the same object with the same plots. Arguments: new_params - list of 4 values to determine how we sweep. In order, must be [ start value, stop value, step, frequency ] """ # Set our new values if desired if new_params is not None: self.start = new_params[0] self.stop = new_params[1] self.step = new_params[2] self.inter_delay = 1 / new_params[3] # Reset our setpoint self.setpoint = self.start - self.step # Reset our plots if self.plot is True: self.setaxline.set_xdata(np.array([])) self.setaxline.set_ydata(np.array([])) self.setax.relim() self.setax.autoscale_view() for i, p in enumerate(self._params): self.plines[i][0].set_xdata(np.array([])) self.plines[i][0].set_ydata(np.array([])) self.plines[i][1].set_xdata(np.array([])) self.plines[i][1].set_ydata(np.array([])) self.axes[i].relim() self.axes[i].autoscale_view() def get_measurement(self): """ Returns the measurement object. """ return self.meas def save(self): """ Saves the plots as a png (may not work? untested) """ b = io.BytesIO() self.fig.savefig(b, format='png')
def save_segmented_data_return_info( self, segment_db_name: str, segment_db_folder: Optional[str] = None, ) -> Dict[int, Dict[str, Dict[str, Tuple[float, float]]]]: """ Save each mesh in a new dataset in given databases returns: segment_info = { data_id: { readout_method: {'range_x': (), 'range_y': () } } } """ if segment_db_folder is None: segment_db_folder = nt.config["db_folder"] if not self.segmented_data: self.prepare_segmented_data(use_raw_data=True) if not os.path.isfile(os.path.join(segment_db_folder, segment_db_name)): ds = load_by_id(self.qc_run_id) nt.new_database(segment_db_name, db_folder=segment_db_folder) qc.new_experiment(f'segmented_{ds.exp_name}', sample_name=ds.sample_name) original_params = self.qc_parameters segment_info: Dict[int, Dict[str, Dict[str, Tuple[float, float]]]] = {} with nt.switch_database(segment_db_name, segment_db_folder): for segment in self.segmented_data: meas = Measurement() meas.register_custom_parameter( original_params[0].name, label=original_params[0].label, unit=original_params[0].unit, paramtype="array", ) meas.register_custom_parameter( original_params[1].name, label=original_params[1].label, unit=original_params[1].unit, paramtype="array", ) result: List[List[Tuple[str, np.ndarray]]] = [] ranges: Dict[str, Dict[str, Tuple[float, float]]] = {} m_params = [str(it) for it in list(segment.data_vars)] for ip, param_name in enumerate(m_params): coord_names = list(segment.coords) x_crd_name = coord_names[0] y_crd_name = coord_names[1] voltage_x = segment[param_name][x_crd_name].values voltage_y = segment[param_name][y_crd_name].values signal = segment[param_name].values range_x = (np.min(voltage_x), np.max(voltage_x)) range_y = (np.min(voltage_y), np.max(voltage_y)) ranges[param_name] = {} ranges[param_name]["range_x"] = range_x ranges[param_name]["range_y"] = range_y setpoints = self.raw_data[param_name].depends_on meas.register_custom_parameter( original_params[ip+2].name, label=original_params[ip+2].label, unit=original_params[1].unit, paramtype="array", setpoints=setpoints, ) v_x_grid, v_y_grid = np.meshgrid(voltage_x, voltage_y) result.append([(setpoints[0], v_x_grid), (setpoints[1], v_y_grid), (param_name, signal.T)]) with meas.run() as datasaver: for r_i in range(len(self.readout_methods)): datasaver.add_result(*result[r_i]) datasaver.dataset.add_metadata( "snapshot", json.dumps(self.snapshot) ) datasaver.dataset.add_metadata( nt.meta_tag, json.dumps(self.nt_metadata) ) datasaver.dataset.add_metadata( "original_guid", json.dumps(self.guid) ) logger.debug( "New dataset created and populated.\n" + "database: " + str(segment_db_name) + "ID: " + str(datasaver.run_id) ) segment_info[datasaver.run_id] = ranges return segment_info
class BaseSweep(QObject): """ The parent class for the 0D, 1D and 2D sweep classes. The default independent variable for BaseSweep and Sweep0D Measurements is time. Creating an object in a sweep class results in data acquisition and plotting of all followed parameters individually measured against the independent variable. The measured data is transferred in real-time (through QObject slot and signal connections) from the Sweep classes to the Runner and Plotter Threads to organize, save, and live-plot the tracked parameters. Attributes: ----------- _params: Defaults as blank list. Desired QCoDeS parameters should be added using follow_param method. _srs: Defaults as blank list. Used to incorporate lock-in amplifier with measurement. set_param: QCoDeS Parameter to be swept, defaults to None for 0D sweep. inter_delay: Time (in seconds) to wait between data points. save_data: Flag used to determine if the data should be saved or not. plot_data: Flag to determine whether or not to live-plot data x_axis: Defaults to 1 to set as time for 0D; defaults to 0 in 1D. meas: Measurement class from QCoDeS, used to register and follow desired parameters. Default is None until a measurement is created using the create_measurement method. dataset: Stores the data obtained during the measurement. continuous: No effect on Sweep0D. Defaults to False for Sweep1D. plot_bin: Defaults to 1. Used to plot data that has been sent to the data_queue list in the Plotter Thread. is_running: Flag to determine whether or not sweep is currently running. t0: Set to monotonic time when creating Runner Thread. persist_data: Always none except in Sweep2D, takes one set_param, allows sweeping of 2 parameters. datasaver: Initiated by Runner Thread to enable saving and export of data. Methods --------- follow_param(*p) Adds QCoDes parameters from imported drivers to be tracked. remove_param(*p) Removes parameters that have been assigned to be tracked. follow_srs(l, name, gain) Adds SRS lock-in amplifier to keep range consistent. create_measurement() Creates a QCoDeS Measurement Object stop() Stops/pauses the sweep. kill() Ends all threads and closes any active plots. check_running() Returns the status of the sweep. start(persist_data=None, ramp_to_start = False) Creates QCoDeS Measurement, Runner and Plotter Threads, and begins sweep. resume() Restarts the sweep using the start method. get_dataset() Retrieves collected data. receive_dataset(ds_dict) Slot to receive data in dictionary form, reemits received data. update_values() Returns dictionary of updated [parameter:value] pairs, default parameter is time. send_updates() Emits signal containing dictionary of parameter, setpoint, direction, and status. If running Sweep0D, will default to time at one second intervals. clear_plot() Clears any displayed plots. set_plot_bin(pb) Sets value for the Plotter Thread plot bin. set_complete_func(func) Sets function to call when sweep is completed. no_change(*args, **kwargs) Does nothing when sweep is completed. check_params_are_correct() Compares the followed parameters to the previously created measurement parameters. export_json(fn=None) Saves all sweep information, attributes, and parameters of QCoDeS Station as JSON dictionary. import_json(json_dict, station=Station()) Loads previously saved experimental setup. """ update_signal = pyqtSignal(dict) dataset_signal = pyqtSignal(dict) reset_plot = pyqtSignal() add_break = pyqtSignal(int) completed = pyqtSignal() def __init__(self, set_param=None, inter_delay=0.1, save_data=True, plot_data=True, x_axis_time=1, datasaver=None, complete_func=None, plot_bin=1, back_multiplier=1): """ Initializer for both classes, called by BaseSweep.__init__() in Sweep0D and Sweep1D classes. Parameters: --------- _params: Defaults as blank list. Desired QCoDeS parameters should be added using follow_param method. _srs: Defaults as blank list. Used to incorporate lock-in amplifier with measurement. set_param: QCoDeS Parameter to be swept, defaults to None for 0D sweep. inter_delay: Time (in seconds) to wait between data points. save_data: Flag used to determine if the data should be saved or not. plot_data: Flag to determine whether or not to live-plot data x_axis: Defaults to 1 to set as time for 0D; defaults to 0 in 1D. meas: Measurement class from QCoDeS, used to register and follow desired parameters. Default is None until a measurement is created using the create_measurement method. dataset: Stores the data obtained during the measurement. continuous: No effect on Sweep0D. Defaults to False for Sweep1D. plot_bin: Defaults to 1. Used to plot data that has been sent to the data_queue list in the Plotter Thread. is_running: Flag to determine whether or not sweep is currently running. t0: Set to monotonic time when creating Runner Thread. persist_data: Always none except in Sweep2D, takes one set_param, allows sweeping of 2 parameters. datasaver: Initiated by Runner Thread to enable saving and export of data. """ QObject.__init__(self) self._params = [] self._srs = [] self.set_param = set_param if inter_delay is None or inter_delay < 0: inter_delay = 0 self.inter_delay = inter_delay self.save_data = save_data self.plot_data = plot_data self.x_axis = x_axis_time self.back_multiplier = back_multiplier self.direction = 0 self.meas = None self.dataset = None self.continuous = False self.plot_bin = plot_bin self.is_running = False self.t0 = 0 self.persist_data = None self.datasaver = datasaver # Set the function to call when we are finished self.complete_func = complete_func if complete_func is None: complete_func = self.no_change self.completed.connect(complete_func) self.plotter = None self.plotter_thread = None self.runner = None @classmethod def init_from_json(cls, fn, station): """ Initializes QCoDeS station from previously saved setup. """ with open(fn) as json_file: data = json.load(json_file) return BaseSweep.import_json(data, station) def follow_param(self, *p): """ Saves parameters to be tracked, for both saving and plotting data. The parameters must be followed before '_create_measurement()' is called. Parameters: *p: Variable number of arguments, each of which must be a QCoDeS Parameter that is desired to be followed. """ if self.is_running: print("Cannot update the parameter list while the sweep is running.") for param in p: if isinstance(param, list): for l in param: if l not in self._params: self._params.append(l) else: if param not in self._params: self._params.append(param) def remove_param(self, *p): """ Removes parameters that were previously followed. Parameters: *p - Variable number of arguments, each of which must be a QCoDeS Parameter that is currently being tracked. """ if self.is_running: print("Cannot update the parameter list while the sweep is running.") for param in p: if isinstance(param, list): for l in param: self._params.remove(l) else: self._params.remove(param) def follow_srs(self, l, name, gain=1.0): """ Adds an SRS lock-in to ensure that the range is kept correctly. Parameters: l: The lock-in instrument. name: The name of the instrument to be followed. gain: The current gain value. """ if self.is_running: print("Cannot update the srs list while the sweep is running.") if (l, name, gain) not in self._srs: self._srs.append((l, name, gain)) def _create_measurement(self): """ Creates a QCoDeS Measurement object. Controls the saving of data by registering QCoDeS Parameter objects. Registers all desired parameters to be followed. This function will register only parameters that are followed BEFORE this function is called. Returns --------- The measurement object with the parameters to be followed. """ # First, create time parameter self.meas = Measurement() # Check if we are 'setting' a parameter, and register it if self.set_param is not None: self.meas.register_parameter(self.set_param) self.meas.register_custom_parameter('time', label='time', unit='s', setpoints=(self.set_param,)) else: self.meas.register_custom_parameter('time', label='time', unit='s') # Register all parameters we are following for p in self._params: if self.set_param is None: self.meas.register_parameter(p, setpoints=('time',)) else: self.meas.register_parameter(p, setpoints=(self.set_param,)) return self.meas def stop(self): """ Stops/pauses the program from running by setting the is_running flag to false. The is_running flag is checked in every thread's loop to determine whether or not to continue running. When sweep is stopped, all data is updated a final time to ensure all completed measurements are stored. """ if self.save_data and self.runner is not None: self.runner.flush_flag = True if not self.is_running: print("Sweep not currently running. Nothing to stop.") self.is_running = False self.send_updates() def kill(self): """ Ends the threads spawned by the sweep and closes any active plots. """ # Stop any data-taking self.is_running = False # Gently shut down the runner if self.runner is not None: self.runner.flush_flag = True self.runner.kill_flag = True self.runner.quit() if not self.runner.wait(1000): self.runner.terminate() print('forced runner to terminate') self.runner = None self.send_updates() # Gently shut down the plotter if self.plotter is not None: self.plotter_thread.quit() if not self.plotter_thread.wait(1000): self.plotter_thread.terminate() print('forced plotter to terminate') self.close_plots() self.plotter = None def check_running(self): """ Returns the status of the sweep. """ return self.is_running def start(self, persist_data=None, ramp_to_start=False): """ Starts the sweep by creating and running the worker threads. Can be used to both start the program and unpause after calling 'stop()'. Parameters --------- persist_data: Optional argument which allows Sweep2D to sweep two paramters. ramp_to_start: Optional argument which gradually ramps each parameter to the starting point of its sweep. Default is true for Sweep1D and Sweep2D. """ if self.is_running: print("We are already running, can't start while running.") return # Check if we have a measurement object if self.meas is None: self._create_measurement() # Check if our list of parameters is out of date- meaning we started, stopped, updated params, and restarted elif not self.check_params_are_correct(): self._create_measurement() if self.plotter is not None and self.plotter.figs_set is True: self.plotter.clear() # print("reset figs") self.plotter.create_figs() # If we don't have a plotter yet want to plot, create it and the figures if self.plotter is None and self.plot_data is True: self.plotter = Plotter(self, self.plot_bin) self.plotter_thread = QThread() self.plotter.moveToThread(self.plotter_thread) self.plotter.create_figs() self.add_break.connect(self.plotter.add_break) self.reset_plot.connect(self.plotter.reset) # If we don't have a runner, create it and tell it of the plotter, # which is where it will send data to be plotted if self.runner is None: self.runner = RunnerThread(self) self.runner.get_dataset.connect(self.receive_dataset) self.t0 = time.monotonic() if self.plot_data is True: self.runner.add_plotter(self.plotter) # Flag that we are now running. self.is_running = True # Save persistent data from 2D sweep self.persist_data = persist_data # Tells the threads to begin if self.plot_data is True and self.plotter_thread.isRunning() is False: self.plotter_thread.start() elif self.plot_data is True and self.plotter.figs_set is False: # print("somehow here") self.plotter.create_figs() if not self.runner.isRunning(): self.runner.kill_flag = False self.runner.start() def resume(self): """ Restarts the sweep after it has been paused. """ if self.is_running is False: self.start(ramp_to_start=False) self.send_updates(no_sp=True) def get_dataset(self): """ Returns the dataset object which contains the collected data. """ return self.dataset @pyqtSlot(dict) def receive_dataset(self, ds_dict): """ Connects the dataset of Runner Thread to the dataset object of the sweep. Parameters --------- ds_dict: Dataset dictionary passed between Runner Thread and sweep. """ self.dataset = ds_dict self.dataset_signal.emit(ds_dict) def update_values(self): """ Called as Runner Thread loops to update parameter values. Verifies the data to be updated depending on type of sweep. Iterates through data point intervals, assigning collected values to their respective parameters. If data is to be saved, it happens here, and the updated data is emitted to all connected slots. Returns --------- data: A dictionary of tuples with the updated data. Each tuple is of the format (<QCoDeS Parameter>, measurement value). The tuples are passed in order of time, then set_param (if applicable), then all the followed params. """ t = time.monotonic() - self.t0 data = [('time', t)] if self.set_param is not None: sp_data = self.step_param() if sp_data is not None: data += sp_data else: return None persist_param = None if self.persist_data is not None: data.append(self.persist_data) persist_param = self.persist_data[0] for i, (l, _, gain) in enumerate(self._srs): _autorange_srs(l, 3) for i, p in enumerate(self._params): if p is not persist_param: v = safe_get(p) data.append((p, v)) if self.save_data and self.is_running: self.runner.datasaver.add_result(*data) self.send_updates() return data def send_updates(self, no_sp=False): """ Emits the signal after dictionary values are updated by 'update_values'. Parameters --------- no_sp: Represents a 'no setpoints' boolean. Default is False, when true it sets the setpoint key to None in the updated dictionary. """ update_dict = {} if self.set_param is None: update_dict['set_param'] = 'time' update_dict['setpoint'] = time.monotonic() - self.t0 update_dict['direction'] = 0 else: update_dict['set_param'] = self.set_param if not no_sp: update_dict['setpoint'] = self.setpoint else: update_dict['setpoint'] = None update_dict['direction'] = self.direction update_dict['status'] = self.is_running self.update_signal.emit(update_dict) def reset_plots(self): """ Clears the currently displayed plots. """ if self.plotter is not None: self.reset_plot.emit() def close_plots(self): """ Resets the plotter and closes all displayed plots. """ if self.plotter is not None: self.plotter.clear() def set_plot_bin(self, pb): """ Sets value for the Plotter Thread plot bin. Parameters --------- pb: Integer value which determines the amount of data to remain in Plotter's data_queue while sweeping. The data queue is only emptied completely when force is set to True in 'update_plots'. """ self.plot_bin = pb if self.plotter is not None: self.plotter.plot_bin = pb def set_complete_func(self, func, *args, **kwargs): """ Sets a function to be called whenever the sweep is finished. Connects to completed signal for Sweep0D, Sweep1D, and Sweep2D. Parameters --------- func: The function to be called upon completion of the sweep. *args: Arbitrary arguments to be passed to the callback function **kwargs: Arbitrary keyword arguments to be passed to the callback function """ self.complete_func = partial(func, *args, **kwargs) self.completed.connect(self.complete_func) @pyqtSlot() def no_change(self, *args, **kwargs): """ Passed when there is no function to be called on completion. Simply allows the sweep to end when 'complete_func' is set to None. """ pass def check_params_are_correct(self): """ Compares the followed parameters to the measurement parameters. Pulls paramaters from object _params, compares list to parameters found in QCoDeS measurement dictionary. Returns --------- Boolean value for whether or not each followed parameter is a QCoDeS parameter associated with the measurement instrument. """ p_list = [] meas_list = [] # print("our params list") for p in self._params: # print(str(p)) p_list.append(str(p)) p_list.append("time") if self.set_param is not None: p_list.append(str(self.set_param)) # print("measurement param list") for key, val in self.meas.parameters.items(): # print(str(key)) meas_list.append(key) return set(p_list) == set(meas_list) def export_json(self, fn=None): """ Saves sweep attributes and parameters of QCoDeS Station as JSON dictionary. Called to save experimental setup to avoid repetitive setup of commonly used measurement instruments. Parameters --------- fn: Represents optional filename to be opened. A copy of the station information will be saved in this file. Returns --------- Dictionary containing all current instruments, parameters, and sweep attributes. """ json_dict = {} json_dict['class'] = str(self.__class__.__name__) json_dict['module'] = str(self.__class__.__module__) json_dict['attributes'] = {} json_dict['attributes']['inter_delay'] = self.inter_delay json_dict['attributes']['save_data'] = self.save_data json_dict['attributes']['plot_data'] = self.plot_data json_dict['attributes']['plot_bin'] = self.plot_bin if 'Sweep0D' in json_dict['class']: json_dict['set_param'] = None json_dict['attributes']['max_time'] = self.max_time elif 'Sweep1D' in json_dict['class']: json_dict['set_param'] = {} json_dict['set_param']['param'] = self.set_param.name json_dict['set_param']['instr_module'] = self.set_param.instrument.__class__.__module__ json_dict['set_param']['instr_class'] = self.set_param.instrument.__class__.__name__ json_dict['set_param']['instr_name'] = self.set_param.instrument.name json_dict['set_param']['start'] = self.begin json_dict['set_param']['stop'] = self.end json_dict['set_param']['step'] = self.step json_dict['attributes']['bidirectional'] = self.bidirectional json_dict['attributes']['continual'] = self.continuous json_dict['attributes']['x_axis_time'] = self.x_axis elif 'Sweep2D' in json_dict['class']: json_dict['attributes']['outer_delay'] = self.outer_delay json_dict['inner_sweep'] = {} json_dict['inner_sweep']['param'] = self.in_param.name json_dict['inner_sweep']['instr_module'] = self.in_param.instrument.__class__.__module__ json_dict['inner_sweep']['instr_class'] = self.in_param.instrument.__class__.__name__ json_dict['inner_sweep']['instr_name'] = self.in_param.instrument.name json_dict['inner_sweep']['start'] = self.in_start json_dict['inner_sweep']['stop'] = self.in_stop json_dict['inner_sweep']['step'] = self.in_step json_dict['outer_sweep'] = {} json_dict['outer_sweep']['param'] = self.set_param.name json_dict['outer_sweep']['instr_module'] = self.set_param.instrument.__class__.__module__ json_dict['outer_sweep']['instr_class'] = self.set_param.instrument.__class__.__name__ json_dict['outer_sweep']['instr_name'] = self.set_param.instrument.name json_dict['outer_sweep']['start'] = self.out_start json_dict['outer_sweep']['stop'] = self.out_stop json_dict['outer_sweep']['step'] = self.out_step elif 'SimulSweep' in json_dict['class']: json_dict['attributes']['bidirectional'] = self.bidirectional json_dict['attributes']['continual'] = self.continuous json_dict['set_params'] = {} for p, items in self.set_params_dict.items(): json_dict['set_params'][p.name] = items json_dict['set_params'][p.name]['instr_module'] = p.instrument.__class__.__module__ json_dict['set_params'][p.name]['instr_class'] = p.instrument.__class__.__name__ json_dict['set_params'][p.name]['instr_name'] = p.instrument.name json_dict['follow_params'] = {} for p in self._params: json_dict['follow_params'][p.name] = (p.instrument.name, p.instrument.__class__.__module__, p.instrument.__class__.__name__) if fn is not None: with open(fn, 'w') as outfile: json.dump(json_dict, outfile) return json_dict @classmethod def import_json(cls, json_dict, station=Station()): """ Loads previously exported Station setup. Reassigns all dictionary values exported as JSON to their appropriate objects. """ def load_parameter(name, instr_name, instr_type, station): if instr_name in station.components.keys(): if isinstance(station.components[instr_name], instr_type): return station.components[instr_name].parameters[name] for i_name, instr in station.components.items(): if isinstance(instr, instr_type): return instr.parameters[name] sweep_class = json_dict['class'] sweep_module = json_dict['module'] if 'Sweep1D' in sweep_class: sp = json_dict['set_param'] module = importlib.import_module(sweep_module) sc = getattr(module, sweep_class) instr_module = importlib.import_module(sp['instr_module']) instrument = getattr(instr_module, sp['instr_class']) set_param = load_parameter(sp['param'], sp['instr_name'], instrument, station) sweep = sc(set_param, sp['start'], sp['stop'], sp['step'], **json_dict['attributes']) elif 'Sweep0D' in sweep_class: module = importlib.import_module(sweep_module) sc = getattr(module, sweep_class) sweep = sc(**json_dict['attributes']) elif 'Sweep2D' in sweep_class: module = importlib.import_module(sweep_module) sc = getattr(module, sweep_class) in_param = json_dict['inner_sweep'] in_instr_module = importlib.import_module(in_param['instr_module']) in_instrument = getattr(in_instr_module, in_param['instr_class']) inner_param = load_parameter(in_param['param'], in_param['instr_name'], in_instrument, station) out_param = json_dict['outer_sweep'] out_instr_module = importlib.import_module(out_param['instr_module']) out_instrument = getattr(out_instr_module, out_param['instr_class']) outer_param = load_parameter(out_param['param'], out_param['instr_name'], out_instrument, station) inner_list = [inner_param, in_param['start'], in_param['stop'], in_param['step']] outer_list = [outer_param, out_param['start'], out_param['stop'], out_param['step']] sweep = sc(inner_list, outer_list, **json_dict['attributes']) elif 'SimulSweep' in sweep_class: module = importlib.import_module(sweep_module) sc = getattr(module, sweep_class) set_params_dict = {} for p, items in json_dict['set_params'].items(): instr_module = importlib.import_module(items['instr_module']) instrument = getattr(instr_module, items['instr_class']) param = load_parameter(p, items['instr_name'], instrument, station) set_params_dict[param] = {} set_params_dict[param]['start'] = items['start'] set_params_dict[param]['stop'] = items['stop'] set_params_dict[param]['step'] = items['step'] sweep = sc(set_params_dict, **json_dict['attributes']) else: return for p, instr in json_dict['follow_params'].items(): instr_module = importlib.import_module(instr[1]) instrument = getattr(instr_module, instr[2]) param = load_parameter(p, instr[0], instrument, station) sweep.follow_param(param) return sweep def estimate_time(self, verbose=True): return 0 def __del__(self): """ Deletes all child threads and closes all figures. """ self.kill()
class Sweep2D(object): def __init__(self, inner_sweep_parameters, outer_sweep_parameters, freq, follow_param): """ We initialize our 2D sweep by taking in the parameters for each sweep, and the frequency. The inner_sweep_parameters and outer_sweep_parameters MUST be a list, conforming to the following standard: [ <QCoDeS Parameter>, <start value>, <stop value>, <step size> ] Arguments: inner_sweep_parameters - list conforming to above standard for the inner sweep outer_sweep_parameters - list conforming to above standard for the inner sweep freq - the frequency of measurement follow_param - the parameter to be tracked while other two are swept """ # Ensure that the inputs were passed (at least somewhat) correctly if len(inner_sweep_parameters) != 4 or len( outer_sweep_parameters) != 4: raise TypeError( 'For 2D Sweep, must pass list of 4 object for each sweep parameter, \ in order: [ <QCoDeS Parameter>, <start value>, <stop value>, <step size> ]' ) # Save our input variables self.in_param = inner_sweep_parameters[0] self.in_start = inner_sweep_parameters[1] self.in_stop = inner_sweep_parameters[2] self.in_step = inner_sweep_parameters[3] if (self.in_stop - self.in_start) > 0: self.in_step = abs(self.in_step) else: self.in_step = (-1) * abs(self.in_step) self.out_param = outer_sweep_parameters[0] self.out_start = outer_sweep_parameters[1] self.out_stop = outer_sweep_parameters[2] self.out_step = outer_sweep_parameters[3] if (self.out_stop - self.out_start) > 0: self.out_step = abs(self.out_step) else: self.out_step = (-1) * abs(self.out_step) self.out_setpoint = self.out_start - self.out_step self.inter_delay = 1 / freq # Sets a flag to ensure that the figures have been created before trying to plot self.figs_set = False self._params = [] self._sr830s = [] # Add the tracking parameter, and create the measurement self.follow_param(follow_param) self.meas = self._create_measurement(self.out_param, self.in_param) # Create the inner sweep self.inner_sweep = Sweep1D(self.in_param, self.in_start, self.in_stop, self.in_step, freq, meas=self.meas, bidirectional=True, plot=True, auto_figs=False) # Make sure the inner sweep knows what parameter it should be reading self.inner_sweep._params.append(follow_param) # Deal with creating the figures self.create_figs() self.inner_sweep.set_figs(self.fig, self.setax, self.axes) self.t0 = time.monotonic() # The origin of the plot is top left, we want it to be bottom left, # so we keep count of what number sweep we are at to put it into the # matrix backwards self.count = 0 # We want to track the max and min values seen to autorange the heatmap self.max_datapt = float("-inf") self.min_datapt = float("inf") self.pause = False def autorun(self, update_rule=None): """ Run the 2D Sweep. Creates the datasaver and passes it to the iterate function. Updates the plots, and then updates the inner sweep if desired. Arguments: update_rule - function to call, accepting the Sweep1D object as an argument, to determine how the next inner sweep should behave """ # If there is not an update_rule passed, then call our own no_change function # to run the same sweep again if update_rule is None: update_rule = self.no_change with self.meas.run() as datasaver: # Loop until we are done while abs(self.out_setpoint - self.out_stop) > abs( self.out_step / 2) and self.pause is False: self.iterate(datasaver) self.update_heatmap(self.count) update_rule(self.inner_sweep) self.count += 1 datasaver.flush_data_to_database() # Make the plots persist after finishing plotting plt.show() def iterate(self, datasaver): """ This function is one iteration of the outer sweep, which steps the outer parameter, and then runs the inner sweep. Arguments: datasaver - the Runner object created from calling Measurement.run() """ # Step the setpoint, and update the value self.out_setpoint = self.out_step + self.out_setpoint self.out_param.set(self.out_setpoint) # Pause if desired if self.inter_delay is not None: time.sleep(self.inter_delay) # Create our data storage object, which is a list of tuples of the parameter # and its value data = (self.out_param, self.out_setpoint) # We pass to the inner sweep the datasaver, and pass it the information about # the outer sweep parameter self.inner_sweep.autorun(datasaver, data) def ramp_to_zero(self): """ Ramps the parameter down to 0. """ self.out_stop = 0 if self.out_setpoint - self.out_stop > 0: self.out_step = (-1) * abs(self.out_step) else: self.out_step = abs(self.out_step) self.inner_sweep.ramp_to_zero() print(f'Ramping {self.out_param.label} to 0 . . . ') while abs(self.out_setpoint - self.out_stop) > abs(self.out_step / 2): # Step the setpoint, and update the value self.out_setpoint = self.out_step + self.out_setpoint self.out_param.set(self.out_setpoint) # Pause if desired if self.inter_delay is not None: time.sleep(self.inter_delay) print(f'Done ramping {self.out_param.label} to 0!') def pause_run(self): if self.pause is False: self.pause = True else: self.pause = False def create_figs(self): """ Creates default figures for each of the parameters. Plots them in a new, separate window. Also creates a 2D heatmap of the data. """ self.fig = plt.figure( figsize=(4 * (2 + len(self._params) + len(self._sr830s)), 4)) self.grid = plt.GridSpec(4, 1 + len(self._params) + len(self._sr830s), hspace=0) self.setax = self.fig.add_subplot(self.grid[:, 0]) # First, create a plot of the sweeping parameters value against time self.setax.set_xlabel('Time (s)') self.setax.set_ylabel(f'{self.in_param.label} ({self.in_param.unit})') self.setaxline = self.setax.plot([], [])[0] self.axes = [] # Now create a plot for every tracked parameter as a function of sweeping parameter for i, p in enumerate(self._params): self.axes.append(self.fig.add_subplot(self.grid[:, 1 + i])) self.axes[i].set_xlabel( f'{self.in_param.label} ({self.in_param.unit})') self.axes[i].set_ylabel(f'{p.label} ({p.unit})') # Create the heatmap # First, determine the resolution on each axis self.res_in = math.ceil( abs((self.in_stop - self.in_start) / self.in_step)) + 1 self.res_out = math.ceil( abs((self.out_stop - self.out_start) / self.out_step)) + 1 self.heatmap_data = np.zeros((self.res_out, self.res_in)) self.heat_fig = plt.figure(2) self.heatmap = plt.imshow(self.heatmap_data) ax = plt.gca() self.heat_ax = ax plt.ylabel(f'{self.out_param.label} ({self.out_param.unit})') plt.xlabel(f'{self.in_param.label} ({self.in_param.unit})') inner_tick_lbls = np.linspace(self.in_start, self.in_stop, 5) outer_tick_lbls = np.linspace(self.out_stop, self.out_start, 5) ax.set_xticks(np.linspace(0, self.res_in - 1, 5)) ax.set_yticks(np.linspace(0, self.res_out - 1, 5)) ax.set_xticklabels(inner_tick_lbls) ax.set_yticklabels(outer_tick_lbls) divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cbar = plt.colorbar(self.heatmap, cax=cax) cbar.set_label(f'{self._params[0].label} ({self._params[0].unit})') self.figs_set = True def update_heatmap(self, count): # GRAB THE X AND Y DATA HERE forward_line = self.axes[0].get_lines()[0] backward_line = self.axes[0].get_lines()[1] x_data_forward = forward_line.get_xdata() y_data_forward = forward_line.get_ydata() x_data_backward = backward_line.get_xdata() y_data_backward = backward_line.get_ydata() # ADD THE DATA TO THE HEATMAP for i, x in enumerate(x_data_forward): self.heatmap_data[self.res_out - count - 1, i] = y_data_forward[i] if y_data_forward[i] > self.max_datapt: self.max_datapt = y_data_forward[i] if y_data_forward[i] < self.min_datapt: self.min_datapt = y_data_forward[i] self.heatmap.set_data(self.heatmap_data) self.heatmap.set_clim(self.min_datapt, self.max_datapt) self.heat_fig.canvas.draw() self.heat_fig.canvas.flush_events() def no_change(self, sweep): """ This function is the default update_rule function. It simply runs the same sweep over again. """ sweep.reset() def follow_param(self, p): """ This function takes in a QCoDeS Parameter p, and tracks it through each sweep. """ self._params.append(p) def follow_sr830(self, l, name, gain=1.0): """ This function adds an SR830, but (as of now) does not do anything with it. """ self._sr830s.append((l, name, gain)) def _create_measurement(self, *set_params): """ Creates a QCoDeS Measurement object. This controls the saving of data by registering QCoDeS Parameter objects, which this function does. Registers all 'sweeping' parameters (set_params), and all 'tracked' parameters, 'self._params'. Returns the measurement object. """ self.meas = Measurement() for p in set_params: self.meas.register_parameter(p) self.meas.register_custom_parameter('time', label='Time', unit='s') for p in self._params: self.meas.register_parameter(p, setpoints=( *set_params, 'time', )) for l, _, _ in self._sr830s: self.meas.register_parameter(l.X, setpoints=( *set_params, 'time', )) self.meas.register_parameter(l.Y, setpoints=( *set_params, 'time', )) return self.meas
class FollowPlotParams(object): def __init__(self, params, inter_delay=0.01, save_data=False): self._params = [] self.save_data = save_data self.inter_delay = inter_delay self.pause = False for p in params: self._params.append(p) if self.save_data: self._create_measurement() self.create_figs() self.t0 = time.monotonic() def _create_measurement(self): """ Creates a QCoDeS Measurement object. This controls the saving of data by registering QCoDeS Parameter objects, which this function does. Registers all 'tracked' parameters, Returns the measurement object. """ self.meas = Measurement() self.meas.register_custom_parameter('time', label='Time', unit='s') for p in self._params: self.meas.register_parameter(p) return self.meas def create_figs(self): """ Creates default figures for each of the parameters. Plots them in a new, separate window. """ self.fig = plt.figure(figsize=(4 * (2 + len(self._params)), 4)) self.grid = plt.GridSpec(4, 1 + len(self._params), hspace=0) self.setax = [] self.setaxline = [] for i, p in enumerate(self._params): self.setax.append(self.fig.add_subplot(self.grid[:, i])) # First, create a plot of the sweeping parameters value against time self.setax[i].set_xlabel('Time (s)') self.setax[i].set_ylabel(f'{p.label} ({p.unit})') self.setaxline.append(self.setax[i].plot([], [])[0]) def pause_run(self): self.pause = not self.pause def is_paused(self): return self.pause def autorun(self): if self.save_data: with self.meas.run() as datasaver: while self.pause is False: t = time.monotonic() - self.t0 data = [] data.append(('time', t)) for i, p in enumerate(self._params): v = p.get() data.append((p, v)) self.setaxline[i].set_xdata( np.append(self.setaxline[i].get_xdata(), t)) self.setaxline[i].set_ydata( np.append(self.setaxline[i].get_ydata(), v)) self.setax[i].relim() self.setax[i].autoscale_view() if self.save_data: datasaver.add_result(*data) plt.pause(self.inter_delay) def iterate(self): t = time.monotonic() - self.t0 data = [] data.append(('time', t)) for i, p in enumerate(self._params): v = p.get() data.append((p, v)) self.setaxline[i].set_xdata( np.append(self.setaxline[i].get_xdata(), t)) self.setaxline[i].set_ydata( np.append(self.setaxline[i].get_ydata(), v)) self.setax[i].relim() self.setax[i].autoscale_view() if self.save_data: datasaver.add_result(*data) plt.pause(self.inter_delay)
def G_up_B(station, field_max, v_polar, amplitude, stanford_gain_V_ac): #Before using this code change these values according to your own setup : R_polar = 1e6 #value of the polarization resistor now = datetime.now() dt_string = now.strftime("%d/%m/%Y %H:%M:%S") # dd/mm/YY H:M:S print(dt_string) #print date and time of the measurement #Start the measurement meas = Measurement() meas.register_parameter(station.lockin_2.amplitude) meas.register_parameter(station.lockin_2.sine_outdc) meas.register_parameter(station.mag.y_measured) meas.register_parameter(station.lockin_1.Y, setpoints=(station.mag.y_measured,)) meas.register_parameter(station.lockin_2.Y, setpoints=(station.mag.y_measured,)) meas.register_parameter(station.lockin_1.X, setpoints=(station.mag.y_measured,)) meas.register_parameter(station.lockin_2.X, setpoints=(station.mag.y_measured,)) meas.register_custom_parameter("R_ac", unit="Ohm",setpoints=(station.mag.y_measured,)) #Prepare the live plot win = qcm.pyplot.PlotWindow(title="Field Sweep 1D") win.resize(600,400) num_points = 0 array_size = 1 B_array = np.full((1,), np.nan) r_array = np.full((1,), np.nan) plot1 = win.addPlot(title ="R_ac(B)") plotdata = plot1.plot(setpoint_x = B_array) plot1.left_axis.label = "Resistance" plot1.left_axis.units = "Ohms" plot1.bot_axis.label = "B" plot1.bot_axis.units = "T" #Print the main lockin settings print(f'Stanford Gain V_AC ={stanford_gain_V_ac}') time_constant = station.lockin_2.time_constant() print(f'Integration time lockins {time_constant} s') print(f'Frequency Lockin : {station.lockin_2.frequency()} Hz') print(f'Filter lockin 1 : {station.lockin_1.filter_slope()} dB roll off') print(f'Sensitivity lockin 1 : {station.lockin_1.sensitivity()} V') print(f'Filter lockin 2 : {station.lockin_2.filter_slope()} dB roll off') print(f'Sensitivity lockin 2 : {station.lockin_2.sensitivity()} A') #Initialisation of the lockin station.lockin_2.amplitude(amplitude) print(f'V_ac polarization : {amplitude/1e-3} mV') station.lockin_2.sine_outdc(v_polar) print(f'V_dc polarization : {v_polar/1e-3} mV') with meas.run() as datasaver: station.mag.y_target(field_max) station.mag.ramp('simul') while abs(station.mag.y_measured()-field_max)>0.001: time.sleep(5*time_constant) b_y = station.mag.y_measured() voltage_X_AC = station.lockin_1.X()/stanford_gain_V_ac current_X_AC = station.lockin_2.X() voltage_Y_AC = station.lockin_1.Y()/stanford_gain_V_ac current_Y_AC = station.lockin_2.Y() R_ac = voltage_X_AC/current_X_AC datasaver.add_result(("R_ac", R_ac), (station.lockin_2.amplitude, amplitude), (station.lockin_2.sine_outdc, v_polar), (station.mag.y_measured, b_y), (station.lockin_2.Y,current_Y_AC), (station.lockin_1.Y,voltage_Y_AC), (station.lockin_2.X,current_X_AC), (station.lockin_1.X,voltage_X_AC)) B_array[num_points] = b_y r_array[num_points] = R_ac plotdata.xData = B_array plotdata.update(r_array) num_points += 1 if num_points == array_size: array_size *= 2 B_array.resize(array_size) B_array[array_size//2:] = np.nan r_array.resize(array_size) r_array[array_size//2:] = np.nan ID_exp = datasaver.run_id station.lockin_2.sine_outdc(0) win.export('figures/Rac_Field_sweep_1D_ID_exp_'+str(ID_exp)+'.png') plot_by_id(ID_exp)
def RT_LT_ithaco(station, voltage, stanford_gain_V, gain_ithaco): station.dmm1.NPLC(10) station.dmm2.NPLC(10) station.yoko.output('off') station.yoko.source_mode("VOLT") station.yoko.output('on') station.yoko.voltage.step = 1e-3 station.yoko.voltage.inter_delay = 0.0001 meas = Measurement() meas.register_parameter(station.yoko.voltage) meas.register_parameter(station.BlueFors_LD.MC_temp) meas.register_custom_parameter("Counter") meas.register_custom_parameter("Current", unit = "A") meas.register_parameter(station.dmm1.volt) meas.register_parameter(station.dmm2.volt) meas.register_custom_parameter("Resistance", unit = "Ohms", setpoints=(station.BlueFors_LD.MC_temp,)) win = qcm.pyplot.PlotWindow(title="R(T)") win.resize(750,500) num_points = 0 array_size = 1 temp_array = np.full((1,), np.nan) r_array = np.full((1,), np.nan) plot1 = win.addPlot(title="RT 4K - 8mK") plotdata = plot1.plot(setpoint_x=temp_array, color=(0, 0, 255)) plot1.left_axis.label = "Resistance" plot1.left_axis.units = "Ohms" plot1.bot_axis.label = "Temperature" plot1.bot_axis.units = "K" j=0 T = station.BlueFors_LD.MC_temp() with meas.run() as datasaver: while T >0.008: T = station.BlueFors_LD.MC_temp() station.yoko.voltage(voltage) time.sleep(1) volt_p = station.dmm1.volt()/stanford_gain_V curr_p = -station.dmm2.volt()/gain_ithaco station.yoko.voltage(-voltage) time.sleep(1) volt_m = station.dmm1.volt()/stanford_gain_V curr_m = -station.dmm2.volt()/gain_ithaco V_av = (volt_p - volt_m)/2 I_av = (curr_p - curr_m)/2 R_av = V_av/I_av datasaver.add_result((station.yoko.voltage, voltage), (station.BlueFors_LD.MC_temp, T), ("Counter", j), ("Resistance", R_av), (station.dmm1.volt,V_av), ("Current", I_av)) temp_array[num_points] = T r_array[num_points] = R_av plotdata.xData = temp_array plotdata.update(r_array) num_points += 1 if num_points == array_size: array_size *= 2 temp_array.resize(array_size) temp_array[array_size//2:] = np.nan r_array.resize(array_size) r_array[array_size//2:] = np.nan #print((T,R_av)) time.sleep(2) j = j+1 station.yoko.voltage(0)